FROTH PET is a decentralized application (dApp) built on Flow EVM for managing Pet NFTs using the $FROTH (ERC-20) token. The platform combines blockchain technology, smart contracts, and game mechanics to create an interactive experience in managing virtual pets.
FROTH PET enables users to:
- Mint Pet NFTs by paying FROTH tokens and receiving random tiers
- Play Games by consuming pet energy to earn scores and rewards
- Buy Food from the Shop to restore pet energy
- Feed Pets to increase energy and level
- Global Chat to communicate with fellow FROTH holders
Frontend:
- React 18 with Vite
- Tailwind CSS for styling
- wagmi v2 + viem for blockchain interactions
- RainbowKit for wallet connection
- Zustand for state management
- React Router for routing
Backend:
- Node.js + Express.js
- MongoDB for database (off-chain data)
- RESTful API endpoints
Smart Contracts:
- Solidity 0.8.20
- OpenZeppelin Contracts (ERC-721, Ownable)
- Hardhat for development and deployment
- Flow EVM Mainnet (Chain ID: 747)
froth-pet/
โโโ contracts/ # Smart contracts (Solidity)
โ โโโ contracts/
โ โ โโโ PetNFT.sol # ERC-721 NFT contract
โ โ โโโ Shop.sol # Food shop contract
โ โ โโโ IFroth.sol # FROTH token interface
โ โ โโโ libs/
โ โ โโโ WeightedRand.sol # Weighted random tier selection
โ โโโ scripts/
โ โโโ deploy.js # Deploy contracts
โ โโโ full-deploy-and-test.js
โ
โโโ frontend/ # React frontend application
โ โโโ src/
โ โ โโโ components/ # UI components
โ โ โ โโโ Header.jsx
โ โ โ โโโ NavBar.jsx
โ โ โ โโโ KittyCard.jsx
โ โ โ โโโ EnergyBar.jsx
โ โ โ โโโ Inventory.jsx
โ โ โ โโโ Leaderboard.jsx
โ โ โ โโโ FrothRun.jsx # Game component
โ โ โโโ pages/ # Page components
โ โ โ โโโ Pet.jsx # Pet management page
โ โ โ โโโ Mint.jsx # Mint NFT page
โ โ โ โโโ Shop.jsx # Shop page
โ โ โ โโโ Game.jsx # Game list page
โ โ โ โโโ GameDetail.jsx # Game detail page
โ โ โ โโโ PlayGame.jsx # Game play page
โ โ โ โโโ Obrolan.jsx # Chat page
โ โ โโโ hooks/ # Custom React hooks
โ โ โ โโโ useMintPet.js # Mint NFT logic
โ โ โ โโโ useBuyFood.js # Buy food logic
โ โ โ โโโ useFeed.js # Feed pet logic
โ โ โ โโโ usePetNFTs.js # Fetch user NFTs
โ โ โ โโโ useFrothBalance.js
โ โ โโโ lib/ # Utility libraries
โ โ โ โโโ contracts.js # Contract ABIs & addresses
โ โ โ โโโ mongodb.js # Backend API calls
โ โ โ โโโ wagmi.js # Wagmi configuration
โ โ โโโ state/
โ โ โโโ useStore.js # Zustand store
โ โโโ public/
โ โโโ nft-images/ # NFT image assets
โ
โโโ backend/ # Node.js API server
โโโ server.js # Express server & API routes
โโโ package.json
| Contract | Address | Purpose |
|---|---|---|
| FROTH Token | 0xb73bf8e6a4477a952e0338e6cc00cc0ce5ad04ba |
ERC-20 token for payments |
| PetNFT | 0xF005c5E8c7a802cf3F6CBcd3445d4F926A01b742 |
ERC-721 NFT contract for Pet |
| Shop | 0x2799E0687EDB8261C19c35f0A3c66De1b1acAb5C |
Contract for buying food |
Treasury Address: 0x24b416d306c231341126c8db74a61221e7ca530b
The treasury wallet is the address that receives all FROTH payments from:
- Mint Pet NFT (10 FROTH per mint)
- Food purchases in the Shop
Contract Name: FROTH PET
Symbol: FPET
Standard: ERC-721
Main Features:
mintWithFroth(uint256 price): Mint a new pet with FROTH paymentfeed(uint256 tokenId, uint8 foodType): Restore pet energyspendEnergy(uint256 tokenId, uint8 cost): Reduce energy for playing gamesrename(uint256 tokenId, string newName): Change pet namegetPet(uint256 tokenId): Get pet data (level, energy, tier, name)
Tier Distribution (Weighted Random):
- Common: 70% (4 different images)
- Uncommon: 20% (3 different images)
- Epic: 7% (2 different images)
- Legendary: 3% (1 image)
Mint Price: 10 FROTH
Pet Attributes:
level: Pet level (starts from 1)energy: Pet energy (0-100)tier: Pet tier (Common, Uncommon, Epic, Legendary)imageURI: Pet image URI (IPFS or HTTP)name: Pet name (default: "FROTH Pet #")
Main Features:
buyFood(uint8 foodType, uint256 quantity): Buy food with FROTHuseFood(uint256 tokenId, uint8 foodType): Use food to feed petgetBag(address user): Check user's food inventory
Food Types:
| Type | Name | Price | Energy Restore | Cap |
|---|---|---|---|---|
| 1 | Burger | 2 FROTH | +50 energy | Max 100 |
| 2 | Grilled Chicken | 3 FROTH | +100 energy | Full (always 100) |
Bag Storage:
Food items are stored in the bag[user][foodType] mapping in the smart contract (on-chain).
User โ Frontend (React)
โ
1. User clicks "Mint Pet" on Pet page
โ
2. Frontend calls useMintPet() hook
โ
3. Approve FROTH token to PetNFT contract
โ (Transaction 1: Approve)
4. Call mintWithFroth(10 FROTH) on PetNFT contract
โ (Transaction 2: Mint)
5. Smart contract:
- Transfer 10 FROTH from user to treasury
- Generate random tier (weighted random)
- Select random image from tier
- Mint ERC-721 token with new tokenId
- Initialize pet with level=1, energy=100
- Emit PetMinted event
โ
6. Frontend:
- Listen for transaction receipt
- Parse PetMinted event to get tokenId
- Fetch pet data from contract (getPet)
- Save NFT data to MongoDB via backend API
โ
7. Backend:
- Receive POST /api/nft/save
- Save to MongoDB collection 'nfts'
- Data: tokenId, owner, tier, imageURI, level, energy, name
โ
8. Frontend refreshes NFT list from backend
โ
9. Pet appears in UI
On-Chain Data:
- Token ownership (ERC-721)
- Pet attributes (level, energy, tier, name)
- Image URI
Off-Chain Data (MongoDB):
- Complete metadata for fast queries
- Historical data
- Additional attributes for UI
User โ Frontend (Shop Page)
โ
1. User selects food (Burger/Chicken) and quantity
โ
2. Frontend calls useBuyFood() hook
โ
3. Calculate total price = pricePerUnit ร quantity
โ
4. Approve FROTH token to Shop contract
โ (Transaction 1: Approve)
5. Call buyFood(foodType, quantity) on Shop contract
โ (Transaction 2: Buy)
6. Smart contract:
- Transfer FROTH from user to treasury
- Increment bag[user][foodType] += quantity
- Emit FoodPurchased event
โ
7. Frontend:
- Listen for transaction receipt
- Read bag from contract (getBag)
- Update local state with on-chain data
- Optional: Sync to MongoDB via backend (optional, for tracking)
โ
8. UI update: Food inventory increases
Storage:
- On-Chain:
bag[user][foodType]in Shop contract (source of truth) - Off-Chain (Optional): MongoDB for tracking and analytics
User โ Frontend (Pet Page)
โ
1. User clicks "Feed" and selects food
โ
2. Frontend calls useFeed() hook
โ
3. Frontend calls backend API: POST /api/nft/feed
โ
4. Backend:
- Validate: Check food exists in bag (via Shop contract or MongoDB)
- Call Shop.useFood(tokenId, foodType) โ Transfer on-chain
- Call PetNFT.feed(tokenId, foodType) โ Update energy on-chain
- Update MongoDB: Decrease bag[user][foodType], update pet energy
โ
5. Frontend refresh:
- Fetch updated pet data from contract
- Fetch updated bag from contract
- Update UI
Dual Update:
- On-Chain: Shop contract decreases bag, PetNFT contract increases energy
- Off-Chain: MongoDB sync for data consistency
User โ Frontend (Game Page)
โ
1. User selects game (FROTH RUN)
โ
2. Frontend checks: Pet energy >= cost (20-30 energy)
โ
3. User clicks "Play Game"
โ
4. Frontend calls backend: POST /api/nft/spend-energy
โ
5. Backend:
- Call PetNFT.spendEnergy(tokenId, cost) โ Decrease energy on-chain
- Update MongoDB: Update pet energy
โ
6. Frontend:
- Launch game (FrothRun.jsx canvas game)
- User plays and earns score
โ
7. After game ends:
- Submit score to backend: POST /api/leaderboard/submit
- Backend saves to MongoDB collection 'leaderboard'
- Frontend refreshes leaderboard
Game Features:
- FROTH RUN: Endless runner game with obstacles
- Dynamic difficulty: Speed and obstacle spawn rate increase with score
- Obstacle types: Ground obstacles, spikes, flying fireballs
- Score tracking: Leaderboard per game
โโโโโโโโโโโโโโโ
โ User โ
โ (Browser) โ
โโโโโโโโฌโโโโโโโ
โ
โ Web3 Connection (wagmi)
โ
โโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Frontend (React) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Hooks (useMintPet, etc) โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโผโโโโโโโโโโโโโโโ โ
โ โ wagmi + viem โ โ
โ โ (Blockchain Interface) โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โ
โ RPC Calls
โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โ Flow EVM Blockchain โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ PetNFT Contract โ โ
โ โ Shop Contract โ โ
โ โ FROTH Token Contract โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ Events & State
โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โ Backend API (Express) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ REST API Endpoints โ โ
โ โ - /api/nft/* โ โ
โ โ - /api/shop/* โ โ
โ โ - /api/leaderboard/* โ โ
โ โ - /api/chat/* โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โ
โ MongoDB Queries
โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โ MongoDB Database โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Collections: โ โ
โ โ - nfts โ โ
โ โ - bags โ โ
โ โ - leaderboard โ โ
โ โ - chat โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Production: Set via
VITE_API_URLenvironment variable - Local:
http://localhost:3001/api
POST /api/nft/save
- Save NFT data to MongoDB
- Body:
{ tokenId, owner, tier, imageURI, metadataURI, level, energy, name }
GET /api/nft/:tokenId
- Get NFT data by tokenId
GET /api/nft/owner/:owner
- Get all NFTs owned by wallet address
PATCH /api/nft/:tokenId
- Update NFT data (level, energy, name)
DELETE /api/nft/:tokenId
- Delete NFT from database
GET /api/bag/:walletAddress
- Get user's food inventory
POST /api/shop/sync-bag
- Sync bag data from contract to MongoDB
GET /api/leaderboard/:gameId
- Get leaderboard for specific game
POST /api/leaderboard/submit
- Submit new score
- Body:
{ walletAddress, gameId, score, petTokenId }
GET /api/chat/messages?limit=100
- Get chat messages (sorted by newest first)
POST /api/chat/message
- Send chat message
- Body:
{ sender, message, walletAddress } - Note: Frontend must verify FROTH balance before sending
GET /api/health
- Server health check
GET /api
- API information and list of endpoints
- Node.js 18+
- npm or yarn
- MetaMask wallet extension
- MongoDB Atlas account (for production) or local MongoDB
# Contracts
cd contracts
npm install
# Frontend
cd ../frontend
npm install
# Backend
cd ../backend
npm installCreate frontend/.env file:
# Flow EVM Network
VITE_EVM_CHAIN_ID=747
VITE_EVM_RPC_URL=https://mainnet.evm.nodes.onflow.org
VITE_BLOCK_EXPLORER_URL=https://flowscan.org
# Contract Addresses
VITE_FROTH_ADDRESS=0xb73bf8e6a4477a952e0338e6cc00cc0ce5ad04ba
VITE_PET_NFT_ADDRESS=0xF005c5E8c7a802cf3F6CBcd3445d4F926A01b742
VITE_SHOP_ADDRESS=0x2799E0687EDB8261C19c35f0A3c66De1b1acAb5C
# Backend API URL
VITE_API_URL=http://localhost:3001/api
# WalletConnect
VITE_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id_hereCreate backend/.env file:
PORT=3001
MONGODB_URI=mongodb+srv://username:[email protected]/?appName=Cluster0Create contracts/.env file:
RPC_URL=https://mainnet.evm.nodes.onflow.org
CHAIN_ID=747
PRIVATE_KEY=0x... # Deployer private key
FROTH_ADDRESS=0xb73bf8e6a4477a952e0338e6cc00cc0ce5ad04ba
TREASURY_ADDRESS=0x24b416d306c231341126c8db74a61221e7ca530bcd contracts
# Compile contracts
npm run compile
# Deploy to Flow EVM Mainnet
npx hardhat run scripts/full-deploy-and-test.js --network flowEVMAfter successful deployment, copy contract addresses to frontend/.env.
- Open MetaMask โ Settings โ Networks โ Add Network
- Fill in:
- Network Name: Flow EVM Mainnet
- RPC URL:
https://mainnet.evm.nodes.onflow.org - Chain ID:
747 - Currency Symbol: FLOW
- Block Explorer:
https://flowscan.org
Double-click START_ALL.bat file in root folder.
Terminal 1 - Backend:
cd backend
npm run devBackend runs on http://localhost:3001
Terminal 2 - Frontend:
cd frontend
npm run devFrontend runs on http://localhost:3000
{
tokenId: String, // Unique token ID
owner: String, // Wallet address
tier: String, // "common", "uncommon", "epic", "legendary"
imageURI: String, // IPFS or HTTP URL
metadataURI: String, // Token URI
level: Number, // Pet level (default: 1)
energy: Number, // Pet energy (0-100)
name: String, // Pet name
createdAt: Date,
updatedAt: Date
}{
walletAddress: String, // Unique wallet address
burger: Number, // Quantity of burgers
ayam: Number, // Quantity of grilled chicken
updatedAt: Date
}{
walletAddress: String,
gameId: String, // Game identifier (e.g., "froth-run")
score: Number, // High score
petTokenId: String, // Pet used for this score
createdAt: Date
}{
sender: String, // Shortened address (e.g., "0x1234...5678")
walletAddress: String, // Full wallet address (lowercase)
message: String, // Chat message
createdAt: Date // ISO timestamp
}- Open dApp in browser
- Click "Connect Wallet" (RainbowKit button)
- Select MetaMask and approve connection
- Ensure the selected network is Flow EVM Mainnet
- Navigate to Pet page
- If you don't have a pet, click "Mint Pet" button
- Approve FROTH transaction (10 FROTH)
- Wait for transaction to succeed
- Pet will appear with random tier (Common, Uncommon, Epic, or Legendary)
- Navigate to Shop page
- Select food:
- Burger (2 FROTH, +50 energy)
- Grilled Chicken (3 FROTH, +100 energy)
- Enter quantity
- Click "Buy" and approve FROTH transaction
- Food will be added to inventory (Bag)
- Navigate to Pet page
- Click "Feed" button
- Select food from inventory
- Approve transaction (if required)
- Pet energy will increase according to selected food
- Navigate to Game page
- Select "FROTH RUN" game
- Click "Play" (will consume pet energy)
- Play the game and avoid obstacles
- Score will be saved to leaderboard
- Navigate to Chat page
- If FROTH balance > 0, chat will be accessible
- If not, "Chat Locked" popup will appear
- Send messages to global chat room
-
On-Chain Source of Truth:
- NFT ownership and pet attributes are stored on-chain
- Bag (food inventory) is stored on-chain in Shop contract
- MongoDB is only for caching and fast queries
-
Validation:
- Backend validates wallet address format
- Frontend verifies FROTH balance before accessing chat
- Smart contracts validate all inputs and permissions
-
CORS:
- Backend configured to allow all origins (production)
- For production, consider whitelisting specific domains
-
Environment Variables:
- Do not commit
.envfiles to repository - Use environment variables in Vercel/Railway for production
- Do not commit
- Mint Price: 10 FROTH per pet NFT
- Food Prices: Burger 2 FROTH, Grilled Chicken 3 FROTH
- Energy Cap: Maximum 100 energy
- Tier Distribution: Common (70%), Uncommon (20%), Epic (7%), Legendary (3%)
- Game Energy Cost: 20-30 energy per game session
cd frontend
vercel login
vercel --prodSet environment variables in Vercel dashboard:
VITE_API_URLVITE_PET_NFT_ADDRESSVITE_SHOP_ADDRESSVITE_FROTH_ADDRESSVITE_WALLETCONNECT_PROJECT_ID(obtain from https://cloud.walletconnect.com)
cd backend
railway login
railway init
railway upSet environment variables in Railway dashboard:
MONGODB_URIPORT
MIT
- Flow EVM Docs: https://developers.flow.com/
- wagmi Docs: https://wagmi.sh/
- RainbowKit Docs: https://www.rainbowkit.com/
- Flowscan Explorer: https://flowscan.org
Built for the $FROTH community