How to Build an ERC-20 Staking App

How to Build an ERC-20 Staking App

Prerequisites

Before we start, make sure you have the following:

  • A web3 wallet like MetaMask
  • An account on thirdweb to deploy contracts

Video Tutorial

Step 1: Deploy token contracts

We'll start by deploying two ERC20 token contracts:

  1. Staking token - the token that users stake
  2. Reward token - the token users earn as rewards for staking

Head to the thirdweb dashboard, connect your wallet, and deploy the standard ERC20 token contract twice. Configure each with a name, symbol, and optional metadata.

For the staking token, mint an initial supply to your wallet (e.g. 1,000,000 tokens). For the reward token, mint an initial supply as well (e.g. 1,000,000).

Make note of the contract addresses for each token, we'll need them later.

Step 2: Deploy staking contract

Next, we'll deploy thirdweb's ERC20 Staking smart contract. This pre-built contract has all the required functionality for staking ERC20 tokens and earning ERC20 reward tokens.

On the contract page, hit 'Deploy Now' and configure the contract with:

  • Your staking token contract address
  • Your reward token contract address
  • Time unit for rewards (in seconds)
  • Reward ratio (tokens earned per token staked)

Deploy the contract to the same network as your tokens. Copy the staking contract address.

Step 3: Deposit reward tokens

The staking contract needs an initial deposit of reward tokens to later distribute to stakers.

To deposit:

  1. Approve the staking contract to transfer reward tokens on your behalf. Call the approve function on the reward token contract.
  2. Call the deposit function on the staking contract, passing in the amount of reward tokens to deposit (e.g. 500,000).

Step 4: Create the staking app

Now we're ready to build the front-end! We'll use Next.js and the thirdweb React SDK.

Create a new Next.js project using the thirdweb CLI:

npx thirdweb create app

Choose Next.js and TypeScript when prompted. Open the project in your code editor.

Configure the app

In the _app.tsx file, wrap your app with the thirdweb provider and set the activeChain to the network you deployed your contracts to:

const activeChain = 'mumbai';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThirdwebProvider activeChain={activeChain}>
        <Component {...pageProps} />
    </ThirdwebProvider>
  );
}

export default MyApp;

Create a const folder and add a file addresses.ts to store your contract addresses:

export const STAKING_CONTRACT_ADDRESS = // your staking contract address
export const STAKING_TOKEN_ADDRESS = // your staking token address
export const REWARD_TOKEN_ADDRESS = // your reward token address  

Create navigation

Add a components folder and create a Navbar.tsx component:

import { ConnectWallet } from '@thirdweb-dev/react';
import Link from 'next/link';
import { useAddress } from '@thirdweb-dev/react';

export function Navbar() {
  const address = useAddress();
  return (
    <div>
      <Link href='/'>
        ERC20 Staking App
      </Link>
      <ConnectWallet />
      {address && (
        <Link href={`/profile/${address}`}>
          Profile
        </Link>
      )}
    </div>
  )
}

This navbar includes a connect wallet button and link to a user profile page if a wallet is connected.

Add the navbar to the _app.tsx file above the Component.

Create the staking page

In the pages/index.tsx file, we'll display info about the user's token balances and add components for staking.

First, check if a wallet is connected using useAddress:

import { useAddress } from '@thirdweb-dev/react';

export default function Home() {
  const address = useAddress();
  
  if (!address) {
    return (
      <div>
        Please connect your wallet
      </div>
    )
  }
  
  return (
    // staking page
  )
}

Create two components in the components folder:

  1. StakeToken.tsx - display staking token balance
  2. RewardToken.tsx - display reward token balance

In each component:

  • Get token contract using useContract
  • Get balance using useTokenBalance
  • Display token symbol and balance

For example:

import { useContract, useTokenBalance } from '@thirdweb-dev/react';
import { STAKING_TOKEN_ADDRESS } from '../const/addresses';

export default function StakeToken() {
  const { contract: stakingToken } = useContract(STAKING_TOKEN_ADDRESS);
  const { data: balance } = useTokenBalance(stakingToken, address);
  
  return (
    <div>  
      <h2>Staking Token</h2>
      <p>{balance?.symbol}</p>
      <p>{balance?.displayValue}</p>
    </div>
  )
}

Add these components to the staking page in a grid layout.

Finally, create a Stake.tsx component to handle staking and claiming rewards. This component will:

  • Get staking token and reward token balances
  • Display current amount staked and rewards available
  • Allow entering amount to stake/unstake
  • Stake, unstake, and claim rewards

Compose the stake, unstake, and claim sections in the component. For example:

<div>
  <h2>Stake Tokens</h2>
  <input 
    type='number'
    value={stakeAmount}
    onChange={(e) => setStakeAmount(e.target.value)}
  />
  <Web3Button
    contractAddress={STAKING_CONTRACT_ADDRESS}
    action={async () => 
      await stakingContract.call(
        'stake',
        ethers.utils.parseEther(stakeAmount)
      )
    }
  >
    Stake
  </Web3Button>
</div>

Add the Stake component to the staking page. That completes the basic app!

Step 5: Test it out

Run yarn dev to start the app locally. Connect your wallet, stake some tokens, and watch your reward balance increase over time. Claim the rewards and unstake tokens as desired.

Step 6: Deploy your app

To deploy your staking app and make it accessible to others, you can use hosting services like Vercel or Netlify that integrate seamlessly with Next.js.

  1. Push your code to a GitHub repository
  2. Connect your repository to Vercel/Netlify
  3. Configure the deployment settings (e.g. environment variables for contract addresses)
  4. Deploy your app!

Your staking app will now have a public URL that you can share with others. As you make updates to your code, you can redeploy the app to see the changes live.

Wrapping Up

There you have it - a fully functional ERC20 staking application! In this guide, we covered how to:

  1. Deploy ERC20 token contracts for staking and rewards
  2. Deploy a robust staking contract
  3. Build a user-friendly frontend for staking

The core technologies we used were:

  • thirdweb to deploy contracts and integrate them into the app
  • Next.js for the frontend framework
  • TypeScript for type-safety

Potential Enhancements

While the app we built covers the core functionality of an ERC20 staking system, there are many ways you could enhance it further:

  • Display user's total rewards earned over time
  • Add a leaderboard of top stakers
  • Allow users to stake multiple tokens into the same contract
  • Implement token gating to restrict access to certain features based on token holdings
  • Incorporate other web3 products like NFTs as staking multipliers or rewards

Feel free to get creative and experiment with adding new features to make your app stand out!

What's next?

By leveraging powerful web3 tools like thirdweb and familiar web2 frameworks like React, we were able to create a complete staking system with a great user experience in a relatively short amount of code.

The skills and concepts you learned in this tutorial can be applied to building all sorts of web3 applications, from NFT minting tools to DAOs to blockchain games. I encourage you to keep experimenting and building in this exciting space!

If you have any other questions, don't hesitate to reach out in the thirdweb Discord. And if this guide was helpful, I'd appreciate you sharing it with others who might be interested in learning web3 development.

If you have any questions or feedback, join the thirdweb Discord or leave a comment below. Thanks for following along, and best of luck with your future projects!