ARTICLE 1: Smart Contracts & Backend Infrastructure
Introduction
In this first part of our three-part series, we'll build the blockchain foundation for a decentralized raffle platform. By the end, you'll have deployed smart contracts to Lisk Sepolia testnet and set up Firebase Cloud Functions to serve blockchain data without gas costs.
What You'll Learn:
- Deploying provably fair raffle contracts
- Creating custom ERC20 test tokens
- Setting up serverless blockchain APIs
- Best practices for contract security
Prerequisites:
- Node.js 18+ installed
- Basic Solidity knowledge
- Firebase account
- Lisk Sepolia testnet ETH (Get from faucet)
Part 1: Understanding the Architecture
The Three-Layer System
┌─────────────────────────────────────────┐
│ Layer 3: Flutter Mobile App │
│ (User Interface - Article 2) │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ Layer 2: Firebase Functions │
│ (Serverless API - This Article) │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ Layer 1: Smart Contracts │
│ (Blockchain Logic - This Article) │
│ ├── RaffleToken (RTKN) │
│ └── SoccerRaffle │
└─────────────────────────────────────────┘
Why This Architecture?
- Layer 1 (Smart Contracts): Handles money and trust — must be on blockchain
- Layer 2 (Cloud Functions): Handles reads — no gas costs for users browsing
- Layer 3 (Flutter App): Handles UX — hides blockchain complexity
Part 2: Smart Contract Development
2.1: The Test Token (RaffleToken)
Most tutorials skip this, but production apps need custom tokens for:
- Testing: Faucet functionality for easy user onboarding
- Control: You manage supply and distribution
- Branding: Custom name/symbol for your platform
Why 6 Decimals?
Real USDT uses 6 decimals (not 18 like ETH). Matching this prevents confusion when users see balances.
Create contracts/RaffleToken.sol
Key Concepts:
-
decimals(): Controls display precision
- 6 decimals → 1 RTKN = 1,000,000 smallest units
- Example: 100.5 RTKN = 100,500,000 units on-chain
-
faucet(): Allows anyone to claim tokens
- Simplifies testing (no buying/transferring needed)
- In production, replace with controlled distribution
2.2: The Raffle Contract (SoccerRaffle)
This is the heart of our platform. It manages all raffle logic with these critical features:
Security Mechanisms
-
Commit-Reveal Randomness
- Creator commits hash of secret seed before raffle ends
- Reveals seed after to generate winner
- Prevents manipulation (can’t pick winner beforehand)
-
Emergency Draw
- If creator doesn’t reveal seed within 1 hour
- Anyone can trigger fallback randomness
- Ensures raffles always conclude
-
ReentrancyGuard
- Prevents reentrancy attacks on token transfers
- Critical for financial contracts
Create contracts/SoccerRaffle.sol
Part 3: Hardhat Setup & Deployment
3.1: Project Initialization
# Create project directory
mkdir soccer-raffle
cd soccer-raffle
# Initialize Node project
npm init -y
# Install Hardhat
npm install --save-dev hardhat
# Initialize Hardhat project
npx hardhat init
# Select: "Create a TypeScript project"
# Install dependencies
npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install @openzeppelin/contracts dotenv
3.2: Configure Hardhat
Create hardhat.config.ts
:
3.3: Create .env
File
# Get private key from MetaMask:
# Settings → Security & Privacy → Show Private Key
DEPLOYER_PRIVATE_KEY=your_private_key_here
LISK_TESTNET_RPC=https://rpc.sepolia-api.lisk.com
⚠️ CRITICAL: Add .env
to .gitignore
immediately!
3.4: Deployment Scripts
Deploy RaffleToken First.
Create scripts/deployRaffleToken.ts
:
3.5: Deploy to Lisk Sepolia
# Step 1: Deploy token
npx hardhat run scripts/deployRaffleToken.ts --network liskTestnet
# Copy the token address and add to .env:
# USDT_ADDRESS=0x07Aa1131A1C06B4680458b0547528272BB603358
# Step 2: Deploy raffle contract
npx hardhat run scripts/deploySoccerRaffle.ts --network liskTestnet
# Step 3: Verify contracts (optional but recommended)
npx hardhat verify --network liskTestnet 0xYOUR_TOKEN_ADDRESS
npx hardhat verify --network liskTestnet 0xYOUR_RAFFLE_ADDRESS 0xYOUR_TOKEN_ADDRESS "Soccer Raffle Token" "SRT"
Expected Output:
✅ RaffleToken deployed at: 0x07Aa1131A1C06B456758b0547528272BB603358
✅ Deployer balance: 10000000.0 RTKN
✅ SoccerRaffle deployed at: 0x5c6A781D663B689b7922A6339AD3eDe910023C6d
Part 4: Firebase Cloud Functions
4.1: Why Cloud Functions?
Problem: If users need ETH just to VIEW raffles, 99% will bounce.
Solution: Firebase Functions act as free RPC proxy.
✅ Users browse raffles with zero gas costs
✅ Only pay gas when joining
✅ Dramatically improves conversion funnel
4.2: Functions Setup
# Install Firebase CLI
npm install -g firebase-tools
# Login
firebase login
# Initialize Firebase in project
firebase init functions
# Select:
# - TypeScript
# - ESLint: Yes
# - Install dependencies: Yes
4.3: Install Dependencies
cd functions
npm install ethers
4.4: Contract ABI Configuration
Create functions/src/contracts/SoccerRaffleABI.ts
:
Key Concept: Human-Readable ABIs
// Traditional (verbose)
{
"name": "getRaffleDetails",
"type": "function",
"stateMutability": "view",
"inputs": [{"name": "raffleId", "type": "uint256"}],
"outputs": [{"name": "creator", "type": "address"}, ...]
}
// Human-readable (clean)
"function getRaffleDetails(uint256 raffleId) view returns (address creator, ...)"
4.5: Cloud Functions Implementation
Create functions/src/index.ts
:
4.6: Deploy Functions
cd functions
npm run build
firebase deploy --only functions
4.7: Test Your Functions
# Test getActiveRaffles
curl https://us-central1-your-project.cloudfunctions.net/getActiveRaffles
# Test getRaffleDetails
curl -X POST \
https://us-central1-your-project.cloudfunctions.net/getRaffleDetails \
-H "Content-Type: application/json" \
-d '{"raffleId": "1"}'
# Test getTokenBalance
curl -X POST \
https://us-central1-your-project.cloudfunctions.net/getTokenBalance \
-H "Content-Type: application/json" \
-d '{"address": "0x74Ba7b9E6B10DE652b1Cbf9d2B187943d7b5C617"}'
Part 5: Creating Test Raffles
5.1: Test Raffle Script
Create scripts/createTestRaffle.ts
:
Run it:
# Add raffle address to .env
RAFFLE_CONTRACT_ADDRESS=0x5c6A781D663B689b7975A6339AD3eDe910023C6d
# Create test raffle
npx hardhat run scripts/createTestRaffle.ts --network liskTestnet
Part 6: Security Best Practices
6.1: Smart Contract Security
✅ Implemented:
- ReentrancyGuard on all state-changing functions
- SafeERC20 for token transfers
- Access control (Ownable)
- Pausable functionality
- Input validation on all parameters
⚠️ Additional Recommendations:
- Professional Audit (OpenZeppelin / Trail of Bits)
- Bug Bounty Program
- Gradual Rollout with low stakes
- Consider Nexus Mutual coverage
6.3: Deployment Checklist
Before Mainnet:
- Smart contracts audited
- All tests passing
- Gas optimization completed
- Admin keys secured (multisig)
- Monitoring via Tenderly/Defender
- Emergency pause tested
- Legal compliance verified
Conclusion: What We’ve Built
Deployed Infrastructure:
- ✅ RaffleToken (RTKN) — 10M supply with 6 decimals, faucet enabled
- ✅ SoccerRaffle Contract — commit-reveal randomness, emergency draw
- ✅ Firebase Cloud Functions — 8 endpoints, zero-gas reads
Key Takeaways:
- Test Tokens Simplify User Onboarding
- Commit-Reveal Is Essential for Fairness
- Cloud Functions Improve UX
- Always Deploy Token First
Next Steps
In Article 2, we’ll build the Flutter mobile app that connects to these contracts:
- Automatic wallet creation
- Seamless token approval
- Real-time transaction monitoring
- Beautiful, gas-free UX
Coming Up:
- Flutter project architecture
- Web3Dart integration
- Riverpod state management
- Transaction lifecycle handling
Resources
Deployed Contracts:
- RaffleToken:
0x07Aa1131A1C06B4680458b0547528272BB603358
- SoccerRaffle:
0x5c6A781D663B689b7975A6339AD3eDe910023C6d
Documentation:
Tools:
Questions or issues? Drop a comment below or open an issue on GitHub!
END OF ARTICLE 1
Top comments (0)