Predict the Powder. Win Big.
Features • Quick Start • Tech Stack • License
Built for Monad Blitz SF 2025 Hackathon
A prediction market platform where users bet on snowfall at ski resorts using blockchain technology on the Monad Testnet. Buy YES or NO shares at fixed prices and win when you predict the powder correctly!
- Prediction Markets: Bet on whether ski resorts will receive target snowfall amounts
- Fixed-Price Shares: YES/NO shares always cost $0.50 USDC each
- Beer Mode: Toggle to view all prices in ski lodge beers instead of USDC (for the true powder hounds)
- Buy Beer with Winnings: Convert your USDC winnings into lodge beer vouchers via x402 micropayments
- Beer Leaderboard: Track who has won the most beers across all users
- Friends Circle: Add wallet addresses with labels, see friends' predictions on each market
- Copy Trading: One-click copy a friend's position or follow your circle's sentiment
- Mobile-Responsive: Fully optimized for mobile devices with touch-friendly UI
- Toast Notifications: Elegant slide-in notifications for all actions
- Weather Forecasts: Live snow forecasts with 24h/48h/7-day predictions
- Resort Webcams: Direct links to live webcams at each resort
- Weather Integration: Real-time weather data from OpenWeather API
- x402 Micropayments: Pay-per-query for detailed market and weather data + beer purchases
- Wallet Integration: Connect via Thirdweb SDK + MetaMask
- Mammoth Mountain
- Palisades Tahoe
- Jackson Hole
- Snowbird
- Aspen
powguess/
├── contracts/ # Solidity smart contracts
│ ├── contracts/ # Source contracts
│ └── remix/ # Flattened contracts for Remix IDE
├── backend/ # Express + TypeScript API server
├── frontend/ # Next.js 14 + Thirdweb frontend
└── README.md
- Node.js 18+
- MetaMask wallet with Monad testnet ETH
- OpenWeather API key (optional, for real weather data)
- Thirdweb client ID (optional, for wallet connection)
cd contracts && npm install
cd ../backend && npm install
cd ../frontend && npm install --legacy-peer-deps| Setting | Value |
|---|---|
| Network Name | Monad Testnet |
| RPC URL | https://testnet-rpc.monad.xyz |
| Chain ID | 10143 |
| Symbol | MON |
| Explorer | https://testnet.monadexplorer.com |
No private key exposure required!
Go to https://remix.ethereum.org
Create two new files in Remix and paste the flattened contract code from:
contracts/remix/MockUSDC_flat.solcontracts/remix/SnowMarket_flat.sol
- Select Solidity compiler version
0.8.20 - Compile both contracts
- In "Deploy & Run" tab, select "Injected Provider - MetaMask"
- MetaMask will prompt to connect - approve it
- Select
MockUSDCcontract - Click Deploy
- Confirm in MetaMask
- Copy the deployed MockUSDC address
- Select
SnowMarketcontract - In the constructor field, paste the MockUSDC address
- Click Deploy
- Confirm in MetaMask
- Copy the deployed SnowMarket address
In Remix, with SnowMarket selected:
Call createMarket for each resort:
| Resort | Parameters |
|---|---|
| Mammoth | "Mammoth Mountain", "Will Mammoth Mountain receive >= 12 inches?", 1200, <timestamp> |
| Palisades | "Palisades Tahoe", "Will Palisades Tahoe receive >= 8 inches?", 800, <timestamp> |
| Jackson Hole | "Jackson Hole", "Will Jackson Hole receive >= 15 inches?", 1500, <timestamp> |
| Snowbird | "Snowbird", "Will Snowbird receive >= 10 inches?", 1000, <timestamp> |
| Aspen | "Aspen", "Will Aspen receive >= 6 inches?", 600, <timestamp> |
Note: <timestamp> should be a future Unix timestamp (e.g., 7 days from now).
Get it from: https://www.epochconverter.com/
After deploying, update the contract addresses:
backend/.env:
SNOW_MARKET_ADDRESS=0x...your_snowmarket_address
MOCK_USDC_ADDRESS=0x...your_mockusdc_addressfrontend/.env.local:
NEXT_PUBLIC_SNOW_MARKET_ADDRESS=0x...your_snowmarket_address
NEXT_PUBLIC_MOCK_USDC_ADDRESS=0x...your_mockusdc_addresscd backend
npm run devBackend runs on http://localhost:3001
cd frontend
npm run devFrontend runs on http://localhost:3000
| Endpoint | Method | Price | Description |
|---|---|---|---|
/api/health |
GET | Free | Health check |
/api/markets |
GET | Free | List all markets |
/api/markets/:id |
GET | $0.001 | Market details |
/api/weather/:resort |
GET | $0.01 | Weather data |
/api/markets/:id/settle |
POST | Admin | Settlement info |
/api/contracts |
GET | Free | Contract addresses |
/api/leaderboard |
GET | Free | Beer leaderboard |
/api/circles/:address |
GET | Free | Get friends circle |
/api/circles/:address/friends |
POST | Free | Add friend to circle |
/api/circles/:address/positions/:marketId |
GET | Free | Friends positions |
Main prediction market contract:
createMarket()- Create new prediction market (owner only)buyShares()- Buy YES or NO shares at $0.50 eachresolveMarket()- Settle market with actual snowfall (owner only)claimWinnings()- Claim winnings after resolutiongetOdds()- Get current market odds
Test USDC token:
- Standard ERC20 with 6 decimals
faucet()- Get 1000 USDC for testingmint()- Owner can mint tokens
To settle a market with actual snowfall data:
- Open Remix IDE with SnowMarket contract
- Connect MetaMask (must be contract owner)
- Call
resolveMarket(marketId, actualSnowfall)actualSnowfallis in inches × 100 (e.g., 12.5 inches = 1250)
- Confirm transaction
| Variable | Description |
|---|---|
PORT |
Server port (default: 3001) |
SNOW_MARKET_ADDRESS |
Deployed SnowMarket address |
MOCK_USDC_ADDRESS |
Deployed MockUSDC address |
OPENWEATHER_API_KEY |
OpenWeather API key |
ADMIN_KEY |
Secret key for API auth |
SKIP_PAYMENTS |
Set to "true" for dev mode |
| Variable | Description |
|---|---|
NEXT_PUBLIC_THIRDWEB_CLIENT_ID |
Thirdweb client ID |
NEXT_PUBLIC_SNOW_MARKET_ADDRESS |
SnowMarket contract address |
NEXT_PUBLIC_MOCK_USDC_ADDRESS |
MockUSDC contract address |
NEXT_PUBLIC_API_URL |
Backend API URL |
- Create Market: Owner creates markets with target snowfall amounts via Remix
- Buy Shares: Users connect wallet and buy YES or NO shares at $0.50 each
- Wait: Market remains open until resolution time
- Settle: Owner settles with actual snowfall data via Remix
- Claim: Winners claim proportional share of the total pool
The API uses x402 micropayments for premium endpoints:
- Market details: $0.001 per query
- Weather data: $0.01 per query
- Buy Beer: $9.00 per lodge beer
For development, SKIP_PAYMENTS=true is set by default.
Winners can convert their USDC winnings into lodge beer vouchers using x402 micropayments!
- Win a Prediction: Correctly predict snowfall and claim your USDC winnings
- Buy Beer: Click "Buy a Lodge Beer with Winnings" button
- Get Voucher: Receive a redemption code for beers at the resort
- Redeem at Lodge: Show your code at the ski lodge bar
| Endpoint | Method | Price | Description |
|---|---|---|---|
/api/buy-beer |
POST | $9/beer | Purchase beer voucher via x402 |
/api/beer-price |
GET | Free | Get beer pricing info |
/api/vouchers/:address |
GET | Free | Get user's vouchers |
/api/vouchers/:id/redeem |
POST | Free | Redeem a voucher |
/api/x402/info |
GET | Free | x402 payment protocol info |
curl -X POST http://localhost:3001/api/buy-beer \
-H "Content-Type: application/json" \
-H "x-402-payment: demo-bypass" \
-d '{"walletAddress": "0x...", "resort": "Mammoth Mountain", "beers": 2}'Response:
{
"success": true,
"voucher": {
"id": "BEER-ABC123",
"beers": 2,
"resort": "Mammoth Mountain",
"redemptionCode": "ZK38-8DU7-QZQK-BN3K",
"totalPaid": "$18",
"message": "Congratulations! You've purchased 2 lodge beers at Mammoth Mountain!"
},
"x402": {
"protocol": "x402",
"amountCharged": 18,
"currency": "USD"
}
}- Open Remix with MockUSDC contract
- Connect your MetaMask
- Call
faucet()function - You'll receive 1000 test USDC
- Smart Contracts: Solidity 0.8.20, OpenZeppelin
- Backend: Express.js, TypeScript, ethers.js, x402
- Frontend: Next.js 14, React, Thirdweb SDK, TailwindCSS
- Blockchain: Monad Testnet
- Weather: OpenWeather API
| Contract | Address |
|---|---|
| SnowMarket | 0xeF92D19dcee0ee22fDd6Ea62634d7FAEe8706d6c |
| MockUSDC | 0xBDB5976d7a9712089c175e62790777EFFC885Eb6 |
Contract Owner: 0xb103a5867d1bf1a4239410c10ec968a5a190231e
-
Go to railway.app and sign in with GitHub
-
New Project → Deploy from GitHub repo → Select
Powguess -
Configure the service:
- Click on the service → Settings tab
- Root Directory:
backend - Build Command:
npm install && npm run build - Start Command:
npm start
-
Add Environment Variables (Variables tab):
PORT=3001 MONAD_RPC_URL=https://testnet-rpc.monad.xyz SNOW_MARKET_ADDRESS=0xeF92D19dcee0ee22fDd6Ea62634d7FAEe8706d6c MOCK_USDC_ADDRESS=0xBDB5976d7a9712089c175e62790777EFFC885Eb6 PAYMENT_ADDRESS=0xb103a5867d1bf1a4239410c10ec968a5a190231e SKIP_PAYMENTS=true -
Deploy - Railway provides a URL like
https://powguess-production.up.railway.app
-
Go to vercel.com and sign in with GitHub
-
Add New Project → Import
Powguess -
Configure:
- Root Directory:
frontend - Framework Preset: Next.js (auto-detected)
- Root Directory:
-
Add Environment Variables:
NEXT_PUBLIC_THIRDWEB_CLIENT_ID=<your-thirdweb-client-id> NEXT_PUBLIC_API_URL=https://<your-railway-url> -
Deploy
- Project Settings → Domains → Add your domain
| Type | Host | Value |
|---|---|---|
| A | @ | 76.76.21.21 |
| CNAME | www | cname.vercel-dns.com |
- In Railway: Settings → Domains → Add
api.yourdomain.com - In DNS: Add CNAME
api→ Railway's provided domain - Update Vercel env:
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
Contributions are welcome! Please feel free to submit a Pull Request.
- Built for Monad Blitz SF 2025 Hackathon
- Powered by Monad - High-performance EVM blockchain
- Wallet integration by Thirdweb
- Weather data from OpenWeather
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❄️ for powder lovers everywhere
