<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikhil Siripurapu</title>
    <description>The latest articles on DEV Community by Nikhil Siripurapu (@nikhil_siripurapu_f15241e).</description>
    <link>https://dev.to/nikhil_siripurapu_f15241e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982973%2F08bec4ca-08ce-40c6-ad74-3f31f10ad986.png</url>
      <title>DEV Community: Nikhil Siripurapu</title>
      <link>https://dev.to/nikhil_siripurapu_f15241e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikhil_siripurapu_f15241e"/>
    <language>en</language>
    <item>
      <title>Building a Full-Stack DeFi Staking Platform on SecureChain AI — A Week 4 Web3 Internship Journey</title>
      <dc:creator>Nikhil Siripurapu</dc:creator>
      <pubDate>Sat, 13 Jun 2026 16:59:52 +0000</pubDate>
      <link>https://dev.to/nikhil_siripurapu_f15241e/building-a-full-stack-defi-staking-platform-on-securechain-ai-a-week-4-web3-internship-journey-32ik</link>
      <guid>https://dev.to/nikhil_siripurapu_f15241e/building-a-full-stack-defi-staking-platform-on-securechain-ai-a-week-4-web3-internship-journey-32ik</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How I designed, deployed, and connected two smart contracts into a live staking dApp — with MetaMask, RainbowKit, and Ethers.js v6&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Four weeks into the EtherAuthority Web3 Internship, and this is the project I'm most proud of — a fully functional &lt;strong&gt;DeFi Staking Platform&lt;/strong&gt; deployed live on &lt;strong&gt;SecureChain AI (SCAI) Mainnet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you've ever used a staking protocol, you know the core idea: deposit tokens, earn yield over time, withdraw whenever you want. Simple on the surface — but building one from scratch, end-to-end, is a completely different story. You have to think about token approvals, reward math, UI state management, wallet connectivity, and security, all at the same time.&lt;/p&gt;

&lt;p&gt;This post breaks down everything I built, every decision I made, and every lesson I learned. By the end, you'll understand not just &lt;em&gt;what&lt;/em&gt; I built, but &lt;em&gt;why&lt;/em&gt; I built it the way I did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://de-fi-staking-platform-vert.vercel.app" rel="noopener noreferrer"&gt;https://de-fi-staking-platform-vert.vercel.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/feudcommon/DeFi-staking-platform" rel="noopener noreferrer"&gt;https://github.com/feudcommon/DeFi-staking-platform&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is DeFi Staking?
&lt;/h2&gt;

&lt;p&gt;Before jumping into code, let's make sure we're on the same page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Staking&lt;/strong&gt; in DeFi means locking (or depositing) tokens into a smart contract in exchange for rewards — usually more tokens — over time. The reward rate is typically expressed as APR (Annual Percentage Rate) or APY (Annual Percentage Yield, which accounts for compounding).&lt;/p&gt;

&lt;p&gt;The key components of any staking platform are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An ERC20 token&lt;/strong&gt; — the token users stake&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A staking contract&lt;/strong&gt; — holds deposited tokens and calculates/distributes rewards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A frontend&lt;/strong&gt; — the UI users interact with to stake, withdraw, and claim&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three have to work together seamlessly. That's what this project is about.&lt;/p&gt;


&lt;h2&gt;
  
  
  System Architecture
&lt;/h2&gt;

&lt;p&gt;Here's the high-level picture of how everything connects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓
MetaMask Wallet (signs transactions)
  ↓
React Frontend (TypeScript + Ethers.js v6)
  ↓              ↓
ERC20 Token   Staking Contract
Contract       (stake, withdraw, claimRewards)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend is the bridge between the user and the blockchain. Every action the user takes — staking, withdrawing, claiming — gets translated into a signed transaction and sent to the appropriate smart contract via Ethers.js.&lt;/p&gt;




&lt;h2&gt;
  
  
  Smart Contracts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Contract 1: ERC20 Token Contract
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Deployed at:&lt;/strong&gt; &lt;code&gt;0x4EF03D37c441BcF78D61367f4EE709027632d929&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the token that users stake. It follows the standard ERC20 interface, which means it has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;balanceOf(address)&lt;/code&gt; — check how many tokens a wallet holds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transfer(to, amount)&lt;/code&gt; — send tokens to another address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;approve(spender, amount)&lt;/code&gt; — authorize another contract to spend your tokens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transferFrom(from, to, amount)&lt;/code&gt; — used by the staking contract to pull tokens from the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;approve&lt;/code&gt; → &lt;code&gt;transferFrom&lt;/code&gt; pattern is critical to understand in DeFi. Because smart contracts can't pull tokens from your wallet without permission, you first have to call &lt;code&gt;approve()&lt;/code&gt; on the token contract to authorize the staking contract to move your tokens. Only &lt;em&gt;then&lt;/em&gt; can the staking contract call &lt;code&gt;transferFrom()&lt;/code&gt; to actually move them.&lt;/p&gt;

&lt;p&gt;This two-step process confuses a lot of users. We'll come back to how I handled this in the frontend.&lt;/p&gt;




&lt;h3&gt;
  
  
  Contract 2: Staking Contract
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Deployed at:&lt;/strong&gt; &lt;code&gt;0x9aab06FAE31e082c26979afca9E53897dB57D50C&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the core of the platform. It handles everything: receiving tokens, tracking who staked how much, calculating rewards, and processing withdrawals.&lt;/p&gt;

&lt;p&gt;Here's the full public interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Deposit ERC20 tokens into the staking contract
function stake(uint amount) external;

// Withdraw previously staked tokens
function Withdraw(uint amount) external;

// Claim accumulated reward tokens
function claimRewards() external;

// Returns staked amount, pending rewards, and APR in one call
function getDashboardData(address user) external view returns (uint, uint, uint);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each function:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;stake(uint amount)&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;When a user calls this, the contract:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calls &lt;code&gt;transferFrom(msg.sender, address(this), amount)&lt;/code&gt; to pull tokens from the user's wallet&lt;/li&gt;
&lt;li&gt;Updates the user's staked balance in a mapping&lt;/li&gt;
&lt;li&gt;Records the timestamp (used for reward calculation later)&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;Withdraw(uint amount)&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This lets users retrieve their tokens at any time — no lock-up period. The contract:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks the user has enough staked&lt;/li&gt;
&lt;li&gt;Calculates any pending rewards before the withdrawal (important — you don't want users to lose unclaimed rewards)&lt;/li&gt;
&lt;li&gt;Transfers the requested amount back to the user&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;claimRewards()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Calculates the rewards earned since the last claim and sends them to the user. Rewards are typically calculated based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much the user has staked&lt;/li&gt;
&lt;li&gt;How long they've been staking&lt;/li&gt;
&lt;li&gt;The reward rate (APR)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple reward formula looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rewards = stakedAmount × APR × (timeElapsed / 365 days)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;getDashboardData(address)&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This is a &lt;code&gt;view&lt;/code&gt; function (no gas cost, no transaction needed) that returns everything the frontend needs in a single call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current staked amount&lt;/li&gt;
&lt;li&gt;Pending rewards&lt;/li&gt;
&lt;li&gt;Current APR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Batching these into one call instead of three separate calls is a small but meaningful optimization — it reduces RPC calls and makes the UI load faster.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why No Lock-Up Period?
&lt;/h3&gt;

&lt;p&gt;I made a deliberate design choice here: &lt;strong&gt;users can withdraw anytime&lt;/strong&gt;. Many staking protocols enforce a lock-up period to incentivize longer commitments. I opted against this for a few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's simpler to audit and understand&lt;/li&gt;
&lt;li&gt;It gives users full control of their funds&lt;/li&gt;
&lt;li&gt;It's more honest — you're not trapping people's tokens&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Security: Smart Contract Audit
&lt;/h2&gt;

&lt;p&gt;Both contracts were audited by &lt;strong&gt;EtherAuthority&lt;/strong&gt; (&lt;a href="https://etherauthority.io" rel="noopener noreferrer"&gt;https://etherauthority.io&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;An audit matters for a few key reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reentrancy attacks&lt;/strong&gt; — a classic DeFi exploit where a malicious contract calls back into your contract before the first execution finishes, draining funds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integer overflow/underflow&lt;/strong&gt; — though Solidity 0.8+ handles this natively, it's still worth verifying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access control&lt;/strong&gt; — making sure only the right addresses can call admin functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reward manipulation&lt;/strong&gt; — ensuring the math can't be gamed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting an audit before going live isn't optional if you care about your users' funds. It's table stakes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frontend: React + TypeScript + Ethers.js v6
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;React 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockchain&lt;/td&gt;
&lt;td&gt;Ethers.js v6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet Connectivity&lt;/td&gt;
&lt;td&gt;RainbowKit + Wagmi + WalletConnect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;SCAI Mainnet (Chain ID: 34)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── contracts/
│   └── config.ts       # ABIs and contract addresses
├── App.tsx             # Main component
├── App.css
└── index.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;config.ts&lt;/code&gt; file is the single source of truth for contract addresses and ABIs. Keeping these centralized means if you redeploy a contract, you only update one file.&lt;/p&gt;




&lt;h3&gt;
  
  
  Connecting to SCAI Mainnet
&lt;/h3&gt;

&lt;p&gt;SCAI Mainnet isn't a default network in MetaMask, so users have to add it. Here are the network details:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network Name&lt;/td&gt;
&lt;td&gt;SCAI Mainnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RPC URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://mainnet-rpc.scai.network&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;34&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Currency Symbol&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SCAI&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block Explorer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://explorer.securechain.ai&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The frontend detects the connected chain ID and prompts users to switch if they're on the wrong network — no silent failures.&lt;/p&gt;




&lt;h3&gt;
  
  
  Multi-Wallet Support with RainbowKit
&lt;/h3&gt;

&lt;p&gt;Instead of building wallet connection from scratch, I used &lt;strong&gt;RainbowKit&lt;/strong&gt; — which gives you MetaMask, WalletConnect, and Coinbase Wallet support out of the box with a polished UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RainbowKitProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDefaultWallets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rainbow-me/rainbowkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureChains&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WagmiConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wagmi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RainbowKit handles the wallet modal, connection state, and chain switching. You just drop in the provider and use the hooks.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Auto-Approval Flow (The Most Important UX Decision)
&lt;/h3&gt;

&lt;p&gt;Here's the part I'm most proud of from a UX standpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; To stake tokens, users need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;approve()&lt;/code&gt; on the ERC20 contract to authorize the staking contract&lt;/li&gt;
&lt;li&gt;Then call &lt;code&gt;stake()&lt;/code&gt; on the staking contract&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you expose this as two separate buttons, you get confused users, failed transactions, and support headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt; Handle it automatically behind the scenes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleStake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 1: Check current allowance&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;STAKING_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: If allowance is insufficient, approve first&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;parsedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;approveTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STAKING_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsedAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;approveTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait for confirmation&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 3: Now stake&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stakeTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stakingContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stakeTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 4: Refresh dashboard data&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchDashboardData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the user's perspective, they just click "Stake" and confirm one or two MetaMask popups. No confusion about what &lt;code&gt;approve&lt;/code&gt; means or why they need to do it first.&lt;/p&gt;

&lt;p&gt;This is the difference between a dApp people actually use and one they bounce off immediately.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Live Dashboard
&lt;/h3&gt;

&lt;p&gt;The dashboard is the heart of the frontend. It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Staked Amount&lt;/strong&gt; — how many tokens the user has deposited&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Available Rewards&lt;/strong&gt; — unclaimed yield accumulated so far&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APR&lt;/strong&gt; — current annual percentage rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APY&lt;/strong&gt; — annualized yield with compounding factored in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallet Balance&lt;/strong&gt; — how many tokens the user holds (not yet staked)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this comes from a single call to &lt;code&gt;getDashboardData()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchDashboardData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;staked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rewards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stakingContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDashboardData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;setStakedAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;staked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;setPendingRewards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rewards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;setApr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nf"&gt;setWalletBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard auto-refreshes after every transaction so users always see up-to-date numbers.&lt;/p&gt;




&lt;h3&gt;
  
  
  Transaction Hash Links
&lt;/h3&gt;

&lt;p&gt;Every transaction surfaces a link to the block explorer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stakingContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Transaction: https://explorer.securechain.ai/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds trust. Users can verify their transaction actually happened on-chain, not just take the UI's word for it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Wrong Network Detection
&lt;/h3&gt;

&lt;p&gt;If a user connects on the wrong chain, the app detects it and shows a prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useNetwork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SCAI_CHAIN_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;SCAI_CHAIN_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WrongNetworkBanner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No silent failures. No confusing errors. Just a clear message: "Please switch to SCAI Mainnet."&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Smart Contracts — Hardhat + SCAI Mainnet
&lt;/h3&gt;

&lt;p&gt;Contracts were compiled and deployed using &lt;strong&gt;Hardhat&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat compile
npx hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; scai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;hardhat.config.js&lt;/code&gt; includes the SCAI network configuration with the RPC URL and deployer private key (from environment variables — never hardcode keys).&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend — Vercel
&lt;/h3&gt;

&lt;p&gt;The React app is deployed on &lt;strong&gt;Vercel&lt;/strong&gt; with zero configuration. Push to &lt;code&gt;main&lt;/code&gt;, it deploys. The live URL is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://de-fi-staking-platform-vert.vercel.app" rel="noopener noreferrer"&gt;https://de-fi-staking-platform-vert.vercel.app&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Run It Locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone the repo&lt;/span&gt;
git clone https://github.com/feudcommon/DeFi-staking-platform.git
&lt;span class="nb"&gt;cd &lt;/span&gt;DeFi-staking-platform

&lt;span class="c"&gt;# 2. Install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# 3. Start the dev server&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;, connect your wallet, switch to SCAI Mainnet, and you're live.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;A few things I'd improve with more time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add a time-weighted rewards model&lt;/strong&gt; — right now rewards are linear. A time-weighted model would reward long-term stakers more, which is better economics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staking tiers&lt;/strong&gt; — higher APR for larger stakes or longer commitments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile responsiveness&lt;/strong&gt; — the UI works on desktop, but could be tighter on mobile&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-based UI updates&lt;/strong&gt; — instead of polling for new data, listen to contract events for real-time updates without constant RPC calls&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The &lt;code&gt;approve&lt;/code&gt; → &lt;code&gt;stake&lt;/code&gt; flow is where most dApp UX breaks.&lt;/strong&gt; Hiding the approval call behind the scenes and automating it completely changed how smooth the experience feels. Always think about what the user actually needs to do, not what the contract requires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;view&lt;/code&gt; functions are free — use them generously.&lt;/strong&gt; Batching all dashboard data into &lt;code&gt;getDashboardData()&lt;/code&gt; instead of making three separate calls made the UI noticeably faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Wrong network detection is non-negotiable.&lt;/strong&gt; If a user is on the wrong chain and tries to interact with your contracts, they'll get confusing errors. Detect it early, surface it clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Always audit before going live.&lt;/strong&gt; Especially when user funds are involved. An audit isn't just about finding bugs — it's about building trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building this staking platform from scratch — two contracts, a full React frontend, multi-wallet support, and live deployment — is the most complete Web3 project I've shipped so far.&lt;/p&gt;

&lt;p&gt;The EtherAuthority internship forced me to actually build and deploy something real every week. That's the only way to learn this stuff. Tutorials can teach you syntax. Shipping teaches you everything else.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built during the EtherAuthority Web3 Internship — Week 4&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Deployed on SecureChain AI (SCAI) Mainnet&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token Contract:&lt;/strong&gt; &lt;code&gt;0x4EF03D37c441BcF78D61367f4EE709027632d929&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Staking Contract:&lt;/strong&gt; &lt;code&gt;0x9aab06FAE31e082c26979afca9E53897dB57D50C&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/feudcommon/DeFi-staking-platform" rel="noopener noreferrer"&gt;https://github.com/feudcommon/DeFi-staking-platform&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://de-fi-staking-platform-vert.vercel.app" rel="noopener noreferrer"&gt;https://de-fi-staking-platform-vert.vercel.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ai</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
