SpinDrop Documentation
Everything you need to deploy, customize, and manage your SpinDrop prize wheel.
01 Overview
SpinDrop is a complete on-chain spin-the-wheel giveaway tool. Deploy your own branded token, configure prizes, and let users spin to win — all fully on-chain with no backend required.
Tech Stack
- Smart Contracts — Solidity 0.8.20 + Hardhat + OpenZeppelin v5
- Frontend — React 19 + TypeScript + Vite 7
- Styling — Tailwind CSS 4
- Wallet — Wagmi v3 + Reown AppKit (300+ wallets)
- Wheel — SVG wheel with Web Audio API sound effects
- Multi-chain — BSC, Ethereum, Polygon, Arbitrum, Base, any EVM
What You Get
- 2 Smart Contracts — ERC-20 token + SpinWheel game
- Full React frontend with animated wheel, prize reveal, confetti, live feed, and admin panel
- Multi-wallet support — MetaMask, TrustWallet, WalletConnect, Coinbase, and 300+ more
- No backend required — fully static + on-chain
- 43 passing smart contract tests covering all functionality
- Built-in error boundary and accessibility features
02 Requirements
Before you begin, make sure you have the following ready:
- Node.js 18+ and npm — download from nodejs.org
- A wallet with native gas tokens for your target chain (BNB, ETH, MATIC, etc.)
- A Reown Project ID (free) — see Section 05 for setup instructions
- Static hosting for the frontend — Vercel, Netlify, cPanel, or any provider that can serve static files
Tip: Use a dedicated deployer wallet. Never use your main wallet's private key.
03 Quick Start
Get up and running in under 10 minutes. Follow these steps in order:
cd contracts && npm install— install contract dependencies- Create
contracts/.envand add yourPRIVATE_KEY npx hardhat compile— compile the smart contractsnpx hardhat run scripts/deploy.js --network bscTestnet— deploy to BSC Testnetcd frontend && npm install— install frontend dependencies- Create
frontend/.envfrom.env.exampleand set yourVITE_REOWN_PROJECT_ID,VITE_TOKEN_ADDRESS, andVITE_WHEEL_ADDRESS npm run build— build the production frontend- Upload the
dist/folder to your hosting provider
Note: The deploy script prints both contract addresses to your terminal. Copy them immediately.
04 Environment Variables
contracts/.env
contracts/.envPRIVATE_KEY=your_wallet_private_key_here
ALCHEMY_KEY=your_alchemy_api_key_here
- PRIVATE_KEY (required) — the private key of the wallet that will deploy and own the contracts
- ALCHEMY_KEY (optional) — only needed if deploying to Ethereum mainnet. Not required for BSC, Polygon, Arbitrum, or Base.
frontend/.env
frontend/.envVITE_REOWN_PROJECT_ID=your_reown_project_id_here
VITE_TOKEN_ADDRESS=0xYourSpinTokenAddress
VITE_WHEEL_ADDRESS=0xYourSpinWheelAddress
VITE_EXPLORER_URL=https://testnet.bscscan.com
VITE_RPC_URL=https://data-seed-prebsc-1-s1.binance.org:8545
VITE_FREE_SPINS_ENABLED=true
- VITE_REOWN_PROJECT_ID (required) — your Reown project ID for wallet connectivity. See Section 05 to obtain one.
- VITE_TOKEN_ADDRESS — deployed SpinToken contract address
- VITE_WHEEL_ADDRESS — deployed SpinWheel contract address
- VITE_EXPLORER_URL — block explorer URL for transaction links
- VITE_RPC_URL — RPC endpoint for your target network
- VITE_FREE_SPINS_ENABLED — set to
falseto disable daily free spins. See Section 10.
05 Reown Project ID
Reown (formerly WalletConnect) provides the wallet connection infrastructure. You need a free Project ID:
- Go to cloud.reown.com
- Create a free account (email or social login)
- Click "Create a new project" from the dashboard
- Copy the Project ID shown on the project settings page
- Paste it into
frontend/.envasVITE_REOWN_PROJECT_ID
Free tier is more than sufficient for most projects. No credit card required.
06 Deploy Contracts
Make sure you have compiled the contracts first (npx hardhat compile) and that your contracts/.env is configured. Then run the deploy command for your target network:
| Network | Command |
|---|---|
| BSC Testnet | npx hardhat run scripts/deploy.js --network bscTestnet |
| BSC Mainnet | npx hardhat run scripts/deploy.js --network bscMainnet |
| Ethereum | npx hardhat run scripts/deploy.js --network ethereum |
| Polygon | npx hardhat run scripts/deploy.js --network polygon |
| Arbitrum | npx hardhat run scripts/deploy.js --network arbitrum |
| Base | npx hardhat run scripts/deploy.js --network base |
After deployment, the script will print two contract addresses to the terminal:
SpinToken deployed to: 0x1234...abcd
SpinWheel deployed to: 0x5678...efgh
Copy both addresses — you will need them in the next step.
Important: Ensure the deployer wallet has enough native gas tokens on the target network before running the deploy script.
07 Update Frontend
After deploying the contracts, update frontend/.env with your deployed addresses and network settings:
VITE_TOKEN_ADDRESS=0xYOUR_DEPLOYED_TOKEN_ADDRESS
VITE_WHEEL_ADDRESS=0xYOUR_DEPLOYED_WHEEL_ADDRESS
VITE_EXPLORER_URL=https://bscscan.com
VITE_RPC_URL=https://bsc-dataseed1.binance.org
Replace the placeholder addresses with the actual addresses printed by the deploy script.
Change Network
If you are targeting a different chain (not BSC), update the networks import in frontend/src/lib/contracts.ts:
import { bsc } from "@reown/appkit/networks"; // or polygon, arbitrum, base, mainnet
export const networks = [bsc];
Then set the matching VITE_EXPLORER_URL and VITE_RPC_URL in your .env file.
08 Customize Wheel Segments
To customize the prizes and their probabilities, edit the segment configuration in contracts/scripts/deploy.js.
Segment Types
- Type 0 — TOKEN: Automatically sends tokens to the winner. You specify the token amount in the segment configuration.
- Type 1 — CUSTOM: Displays a message to the user (e.g., "Whitelist Spot", "Free Merch", etc.). No tokens are sent automatically.
Weights
Each segment has a weight that controls its probability relative to other segments. A higher weight means that segment is more likely to be selected.
For example, if you have three segments with weights [2, 5, 10], their probabilities are approximately 12%, 29%, and 59% respectively.
Constraints
- 2–12 segments per wheel
- Labels — max 50 characters
- Messages — max 200 characters
- Weights — must be greater than 0
Tip: After changing segments, you must redeploy or call setSegments on the deployed contract to apply changes.
09 Token Branding
To change the name, symbol, or initial supply of the token, edit contracts/contracts/SpinToken.sol:
// Change the token name
"SpinDrop Token" → "Your Token Name"
// Change the token symbol
"SPIN" → "YOUR"
// Change the initial supply (default: 1,000,000 tokens)
1_000_000 → 5_000_000
The token has a max supply cap of 1 billion tokens to prevent infinite minting.
After making changes, recompile and redeploy:
npx hardhat compile
npx hardhat run scripts/deploy.js --network yourNetwork
10 Free Spins
By default, SpinDrop gives every connected wallet one free spin per day. After using their free spin, users must wait for the cooldown to expire or pay SPIN tokens. This feature is fully optional and can be disabled.
How It Works
- When a user connects their wallet and has never spun (or the cooldown has expired), the "FREE SPIN" badge appears in the bet panel
- After using their free spin, a countdown timer shows when the next free spin will be available
- The cooldown is enforced on-chain via the
freeCooldownparameter in the SpinWheel contract - Users can always bypass the cooldown by paying the spin cost in SPIN tokens
Disabling Free Spins
To disable free spins entirely so users must always pay SPIN tokens:
Option 1: Frontend only (hides the free spin UI, but the contract still allows them)
frontend/.envVITE_FREE_SPINS_ENABLED=false
This removes the free spin badge and countdown timer from the interface. Users will only see the paid spin option. Rebuild with npm run build after changing.
Option 2: On-chain (fully disables free spins at the contract level)
Set the free spin cooldown to an extremely high value so canSpinFree always returns false:
npx hardhat console --network yourNetwork
const wheel = await ethers.getContractAt("SpinWheel", "0xYOUR_WHEEL_ADDRESS");
// Set cooldown to ~100 years (effectively disabling free spins)
await wheel.setFreeCooldown(3153600000);
Option 3: Both (recommended for production)
Use both options together: disable on the frontend for a clean UI, and disable on-chain so even direct contract calls cannot get free spins.
Re-enabling Free Spins
To re-enable free spins:
- Set
VITE_FREE_SPINS_ENABLED=trueinfrontend/.envand rebuild - Set a normal cooldown on-chain:
await wheel.setFreeCooldown(86400)(24 hours)
Demo mode: The default configuration gives one free spin per 24 hours. This is a good setting for demos and live previews, as it lets visitors try the wheel without needing tokens.
11 Spin Cost & Cooldown
Configure the cost of paid spins and the cooldown between free spins by editing contracts/scripts/deploy.js:
const SPIN_COST = hre.ethers.parseEther("10"); // 10 SPIN tokens per paid spin
const FREE_COOLDOWN = 86400; // 24 hours in seconds
Common Cooldown Values
| Value (seconds) | Duration |
|---|---|
3600 |
1 hour |
21600 |
6 hours |
43200 |
12 hours |
86400 |
24 hours (default) |
604800 |
7 days |
Note: You can also change the spin cost and cooldown via the admin panel after deployment.
12 UI Colors & Theming
Customize the look and feel of the frontend by editing CSS custom properties in frontend/src/index.css:
:root {
--bg-base: #0b0a14; /* Page background */
--gold: #ffd700; /* Primary accent */
--cyan: #00f0ff; /* Secondary accent */
--purple: #a855f7; /* Tertiary accent */
--text-1: #f0ead8; /* Primary text */
--text-2: #8a7d6a; /* Secondary text */
--font-display: "Orbitron"; /* Display font */
--font-body: "Exo 2"; /* Body font */
}
Wallet Modal Accent
Update the wallet modal accent color in frontend/src/main.tsx:
themeVariables: { "--w3m-accent": "#a855f7" }
Background Image
Replace frontend/public/images/casino-bg.jpg with your own image. Recommended resolution is 2752x1536 or similar 16:9 aspect ratio.
After changing theme values, rebuild the frontend with npm run build and re-upload the dist/ folder.
13 Deploy Frontend
The frontend builds to a static dist/ folder that can be hosted anywhere. Below are instructions for the most popular options.
Vercel
- Install the Vercel CLI:
npm i -g vercel - Build the frontend:
cd frontend && npm run build - Deploy:
vercel deploy --prod
Netlify
- Build the frontend:
cd frontend && npm run build - Go to netlify.com/drop and drag the
dist/folder onto the page
cPanel
- Build the frontend:
cd frontend && npm run build - Open File Manager in cPanel and navigate to
public_html - Upload the contents of the
dist/folder (not the folder itself) intopublic_html
Important: Make sure index.html ends up in the root of your hosting directory, not inside a subfolder.
SPA Routing
For single-page application routing, add a redirect rule so all paths serve index.html:
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
Netlify — _redirects
/* /index.html 200
14 Admin Guide
The deployer wallet is automatically set as the contract admin. The Admin panel is visible in the app when connected with the admin wallet — look for the gear icon in the top bar.
Admin Panel Features
- Deposit tokens to the prize pool
- Withdraw tokens from the prize pool
- Change spin cost
- Pause / unpause the contract
- View current segments and weights
Update Segments After Deployment
To change the wheel segments after deployment, use the Hardhat console or a custom script:
npx hardhat console --network yourNetwork
const wheel = await ethers.getContractAt("SpinWheel", "0xYOUR_WHEEL_ADDRESS");
await wheel.setSegments([...newSegments]);
15 Smart Contract Security
SpinDrop contracts are built with multiple layers of security using battle-tested OpenZeppelin libraries:
- ReentrancyGuard — Prevents reentrancy attacks on all external functions
- SafeERC20 — Safe token transfers that check return values
- Pausable — Emergency pause mechanism to halt the contract
- Ownable — Access-controlled admin functions
- Input validation — Constructor validates token address, segment strings have max lengths
- Max supply cap — Token has a 1 billion max supply to prevent infinite minting
Events
The contracts emit events for all key operations, making on-chain activity fully transparent:
- SpinResult — emitted on every spin outcome
- TokensDeposited / TokensWithdrawn — prize pool changes
- SpinCostUpdated / FreeCooldownUpdated — parameter changes
- PrizeNotAwarded — emitted when prize pool has insufficient balance
Tests
The contracts include 43 passing tests covering all functionality including edge cases, access control, and error conditions.
Note on randomness: SpinDrop supports both block.prevrandao for instant on-chain randomness and optional Chainlink VRF v2.5 for provably fair, verifiable randomness. See Section 15 for VRF setup instructions.
16 Chainlink VRF Setup
What is Chainlink VRF?
Chainlink VRF (Verifiable Random Function) is an on-chain randomness solution that generates cryptographically proven random numbers. Each random result comes with a proof that is verified on-chain before the contract accepts it, making it impossible for anyone — including miners, node operators, or the contract owner — to tamper with the outcome. For a spin-the-wheel game, this means every spin result is provably fair and auditable.
Prerequisites
- LINK tokens on your target network — VRF requests are paid in LINK
- For testnet, get free LINK from faucets.chain.link/bnb-chain-testnet
- For mainnet, purchase LINK from any exchange and send to your SpinWheel contract
Setup Steps
- Deploy the contracts — The deploy script automatically detects your network and configures the correct Chainlink VRF wrapper address for supported networks (BSC testnet and BSC mainnet). No manual VRF configuration needed.
- Fund your contract with LINK — Send LINK tokens to your deployed SpinWheel contract address. Each VRF spin costs a small amount of LINK. Start with 1–2 LINK for testing.
- Enable VRF — Toggle VRF on from the admin panel, or call
setVRFEnabled(true)directly on the SpinWheel contract from the owner wallet.
No VRF? No problem. SpinDrop works perfectly without Chainlink VRF out of the box using block.prevrandao for instant randomness. VRF is an optional upgrade you can enable when ready.
How It Works
With VRF enabled, the spin flow changes slightly:
- The player clicks Spin and signs the transaction (1 on-chain transaction)
- The SpinWheel contract sends a randomness request to Chainlink VRF via the VRFV2PlusWrapper (direct funding)
- Chainlink's decentralized oracle network generates a verifiable random number and calls back your contract
- The contract resolves the spin result using the verified random number, and the wheel animation reveals the outcome
The entire process typically completes within a few seconds on BSC, depending on network conditions.
Supported Networks
| Network | VRF Support |
|---|---|
| BSC Testnet | Pre-configured (auto-detected by deploy script) |
| BSC Mainnet | Pre-configured (auto-detected by deploy script) |
| Other EVM Chains | Manually set the VRF wrapper address after deployment |
For VRF wrapper addresses on other chains, see the Chainlink VRF v2.5 Supported Networks documentation.
Disabling VRF
You can disable VRF at any time by toggling it off in the admin panel or calling setVRFEnabled(false) on the contract. The wheel will immediately revert to using instant on-chain randomness via block.prevrandao. No downtime, no redeployment needed.
17 Troubleshooting
"Wallet not connecting"
Verify that VITE_REOWN_PROJECT_ID is set correctly in frontend/.env. After updating the env file, rebuild with npm run build and redeploy.
"Transaction failed"
Ensure the connected wallet has enough native gas tokens (BNB, ETH, MATIC, etc.) and that you are on the correct network matching where the contracts are deployed.
"Wheel not configured"
The deploy script must call setSegments to configure the wheel. If segments are missing, run the deploy script again or manually call setSegments on the contract.
"Insufficient prize pool"
The wheel contract does not have enough tokens to pay out prizes. Deposit more tokens via the Admin panel or by calling depositTokens on the contract.
"Free spin on cooldown"
The free spin cooldown has not expired yet. The default is 24 hours. Wait for the cooldown period to pass, or adjust the FREE_COOLDOWN value and redeploy.
"Admin panel not visible"
The admin panel is only visible when connected with the deployer/owner wallet. Make sure you are connecting with the same wallet that deployed the contracts.
"Prize not awarded"
Check the prize pool balance. The contract emits a PrizeNotAwarded event when the balance is insufficient to cover the prize amount. Deposit more tokens to resolve this.
"Contract paused"
The admin has paused the contract. Connect with the admin wallet and unpause the contract via the Admin panel.
Build errors
Make sure you are running Node.js 18 or later. Try deleting node_modules and package-lock.json, then run npm install again from scratch.