GrimStake
A full-stack Web3 staking application built with Solidity, Hardhat, Next.js, Wagmi, RainbowKit, and viem. Grim Stake is a testnet staking platform.
Gallery
Grim Stake — Testnet Staking dApp
A full-stack Web3 staking application built with Solidity, Hardhat, Next.js, Wagmi, RainbowKit, and viem.
Grim Stake is a testnet staking platform demonstrating:
- ERC-20 staking
- Real-time rewards
- Claiming rewards
- Unstaking
- Pool analytics (TVL, APR, reward rate)
- Auto-adding tokens to MetaMask
Deployed live on Sepolia Testnet.
Preview
Table of Contents
Overview
Architecture
Smart Contracts
- MockERC20Token.sol
- GrimStake.sol
Backend Deployment Guide
Frontend Setup
Frontend Features
Hook Architecture
User Workflow
Project Structure
Running the Frontend
CoinMarketCap Integration
Overview
Grim Stake provides a fully functional staking environment:
- Stake ERC-20 test tokens
- Earn block-based rewards
- Claim and unstake at any time
- Add tokens directly to MetaMask
- Live statistics from contract reads
- Modern UI built with Tailwind, RainbowKit, Framer Motion
All functionality runs on Sepolia Testnet.
Architecture
Backend
- Solidity smart contracts managed via Hardhat
- Pools and reward accounting handled in GrimStake
Frontend
- Next.js dApp using Wagmi v2, viem, and RainbowKit
Smart Contracts
MockERC20Token.sol
A lightweight ERC-20 token with:
- mint(address,uint256)
- approve
- transfer
- transferFrom
Constructor:
constructor(string memory _name, string memory _symbol)
Deploy multiple variants by changing constructor arguments.
GrimStake.sol
Maintains staking pools and rewards.
Pool Structure
Pool {
uint256 totalStaked;
uint256 rewardRatePerSecond;
}
User Structure
UserInfo {
uint256 amount;
uint256 rewardDebt;
}
Key Functions
- addPool(token, rewardRate)
- stake(token, amount)
- unstake(token, amount)
- pendingRewards(user, token)
- claim(token)
- getPoolStats(token)
- getUserStake(user, token)
Backend Deployment Guide
1. Clone the Repository
git clone https://github.com/YOUR_REPO/grim-vault.git
cd grim-vault/grim-contract
npm install
2. Compile Contracts
npx hardhat compile
3. Deploy Using Remix (Testnet)
GrimStake
- Open GrimStake.sol
- Compile
- Select "Injected Provider – MetaMask"
- Deploy and copy the contract address
MockERC20
Deploy multiple tokens as needed:
Example:
name: GrimToken
symbol: GRIM
4. Add Token Pool
addPool(<TokenAddress>, <RewardRatePerSecond>)
Fast-demo example:
1e17
5. Fund the Reward Pool
mint(<admin>, 10000e18)
transfer(<GrimStake>, 5000e18)
Frontend Setup
cd ../frontend
npm install
Wagmi Codegen (wagmi.config.ts):
deployments: {
GrimStake: {
11155111: "0x...grimstake"
}
}
Generate typed contracts:
npx wagmi generate
Creates: src/generated.ts
Frontend Features
- Token selection
- Add token to MetaMask
- Approve + stake
- Unstake
- Claim rewards
- Live pool stats
- User position
- Neon glass UI with animations
Hook Architecture
Hook
Role
useStake
Handles approve + stake lifecycle
useUnstake
Unstaking workflow
useClaimRewards
Reward claims
usePendingRewards
Real-time reward tracking
usePoolStats
TVL, APR, rewardRate
useUserStakeInfo
User stake amount
useApprove
ERC-20 approve using MockERC20 ABI
User Workflow
Connect wallet to Sepolia
Add GRIM token to MetaMask
Mint test tokens
Approve + stake
Watch rewards accumulate
Claim rewards
Unstake
Project Structure
grim-vault/
├─ grim-contract/
│ ├─ contracts/
│ │ ├─ GrimStake.sol
│ │ └─ MockERC20Token.sol
│ ├─ hardhat.config.js
└─ frontend/
├─ src/generated.ts
├─ lib/contracts/
├─ components/
├─ public/
└─ app/
Running the Frontend
npm run dev
URL: http://localhost:3000
CoinMarketCap Integration
Displays:
- Market price
- 24h price change
- Market cap
1. Add keys to .env.local
NEXT_PUBLIC_CMC_API_KEY=<YOUR_API_KEY>
2. Fetch Helper
export async function fetchCMCPrice(symbol: string) {
const key = process.env.NEXT_PUBLIC_CMC_API_KEY;
const res = await fetch(
`https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${symbol}`,
{ headers: { 'X-CMC_PRO_API_KEY': key } }
);
const json = await res.json();
return json.data[symbol];
}
3. Use in Pool Overview
const [price, setPrice] = useState(null);
useEffect(() => {
fetchCMCPrice(selected.symbol).then((p) => {
setPrice(p.quote.USD);
});
}, [selected]);
4. Render
Market Price: $ {price?.price.toFixed(2) ?? "—"}
24h Change: {price?.percent_change_24h.toFixed(2) ?? "—"} %
Market Cap: $ {price?.market_cap.toLocaleString() ?? "—"}