A full-stack Web3 platform that combines raffles with prediction markets (InfoFi) to create structured, fair finite games that convert crypto speculation into quantifiable value.
SecondOrder.fun is a decentralized platform that:
- Hosts raffles where players can win prizes
- Creates associated prediction markets (InfoFi) for each raffle
- Uses hybrid pricing mechanisms combining raffle positions with market sentiment
- Detects arbitrage opportunities in real-time between raffle prices and market predictions
- Provides live updates via on-chain oracle event subscriptions
- Enables cross-layer strategies between raffle and prediction market layers
- React 18
- Vite 6
- TypeScript/JavaScript
- Tailwind CSS
- shadcn/ui component library
- Wagmi for Ethereum interactions
- Viem for Ethereum utilities
- RainbowKit for wallet connections
- Fastify for REST API
- Hono for edge functions and SSE
- Supabase for database
- WebSocket for real-time communication
- Solidity
- Foundry development framework
- OpenZeppelin for secure contract patterns
- Chainlink VRF for random number generation
- Vitest for frontend testing
- Foundry for smart contract testing
sof-alpha/
├── backend/
│ ├── fastify/
│ │ ├── routes/
│ │ └── server.js
│ ├── hono/
│ └── shared/
├── contracts/
│ ├── src/
│ │ ├── core/
│ │ └── infofi/
│ ├── test/
│ ├── script/
│ └── lib/
├── documentation/
├── instructions/
├── public/
├── src/
│ ├── components/
│ ├── contexts/
│ ├── hooks/
│ ├── lib/
│ ├── styles/
│ └── views/
├── tests/
└── documentation/- Node.js >= 18
- Foundry (for smart contract development)
- Git
-
Clone the repository:
git clone https://github.com/SecondOrder-fun/sof-alpha.git cd sof-alpha -
Install frontend dependencies:
npm install
-
Install backend dependencies:
npm install fastify @fastify/cors @fastify/helmet @fastify/rate-limit ws npm install hono npm install @supabase/supabase-js npm install jsonwebtoken
-
Set up smart contract dependencies:
cd contracts forge install openzeppelin/openzeppelin-contracts forge install smartcontractkit/chainlink cd ..
Create a .env file in the root directory with the following variables:
# Wallet Connect
VITE_WALLET_CONNECT_PROJECT_ID=your_wallet_connect_project_id
# Supabase
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_key
# JWT
JWT_SECRET=your_jwt_secret
JWT_EXPIRES_IN=24h
# Chainlink VRF (for smart contracts)
VRF_COORDINATOR=your_vrf_coordinator_address
VRF_KEY_HASH=your_vrf_key_hash
VRF_SUBSCRIPTION_ID=your_vrf_subscription_id
# Deployment
PRIVATE_KEY=your_private_key_for_deploymentImportant: When starting fresh or restarting Anvil, reset the database first to prevent stale data:
# Terminal 1: Start Anvil
npm run anvil
# Terminal 2: Reset database and deploy contracts
npm run reset:local-db # Clear stale data
npm run anvil:deploy # Deploy contracts
npm run dev:backend # Start backend
# Terminal 3: Start frontend
npm run dev:frontendSee Database Reset Guide for details.
npm run devOptional dev-only tooling:
- Agentation is wired at the app root and only renders in development (
import.meta.env.DEV). - Install dependency:
npm install agentation(already in package.json).
This will start the Vite development server on http://localhost:5173.
node backend/fastify/server.jsThis will start the Fastify server on http://localhost:3001.
Compile contracts:
cd contracts
forge buildRun tests:
forge testDeploy contracts (to local network):
forge script script/Deploy.s.sol:DeployScript --rpc-url http://localhost:8545 --private-key $PRIVATE_KEY --broadcastnpm run testcd contracts
forge testThe core raffle contract that handles:
- Creating and managing raffles
- Selling tickets
- Selecting winners using Chainlink VRF
- Distributing prizes
Discrete bonding curve for ticket sales:
- Step-based pricing mechanism
- Buy/sell fee collection (0.1% buy, 0.7% sell)
- Fee accumulation for treasury system
- Season locking capability
Platform token with treasury management:
- ERC20 token with 100M initial supply
- Centralized fee collection system
- Treasury distribution mechanism
- Role-based access control
The prediction market factory contract that handles:
- Auto-creating FPMM-based prediction markets when players cross 1% threshold
- Managing market types using keccak256 hashes for gas efficiency
- Integrating Gnosis Conditional Token Framework
- Providing 100 SOF liquidity per market from treasury
- Resolving markets via RaffleOracleAdapter
Market Type Implementation:
Market types are identified using keccak256 hashes instead of strings for gas efficiency (~200-400 gas savings per event). The contract emits hashes like 0x9af7ac... which the backend decodes to human-readable strings like "WINNER_PREDICTION".
To add new market types:
- Add constant to contract:
bytes32 public constant NEW_TYPE = keccak256("NEW_TYPE"); - Calculate hash:
cast keccak "NEW_TYPE" - Update backend mapping in
backend/src/listeners/marketCreatedListener.js - Redeploy contract and restart backend
See MARKET_TYPE_IMPLEMENTATION.md for detailed guide.
SecondOrder.fun implements a two-stage fee collection and treasury management system:
- Fee Accumulation: Bonding curves accumulate fees from buy/sell transactions
- Fee Extraction: Fees are extracted to SOFToken contract (manual or automated)
- Treasury Distribution: Treasury managers distribute accumulated fees to treasury address
Key Features:
- Minimal gas overhead (~100 gas per transaction)
- Centralized fee aggregation across all platform contracts
- Flexible distribution timing and destinations
- Role-based access control for security
Documentation:
Gas Impact:
- Fee tracking: ~100 gas per buy/sell transaction (<0.1% increase)
- Fee extraction: ~50,000 gas (admin operation, not user-facing)
- Treasury distribution: ~30,000 gas (treasury manager operation)
GET /api/raffles- Get all active rafflesGET /api/raffles/:id- Get a specific rafflePOST /api/raffles- Create a new rafflePOST /api/raffles/:id/join- Join a raffleGET /api/raffles/:id/participants- Get raffle participants
GET /api/infofi/markets- Get all active InfoFi marketsGET /api/infofi/markets/:id- Get a specific InfoFi marketPOST /api/infofi/markets- Create a new InfoFi marketPOST /api/infofi/markets/:id/bet- Place a bet on an InfoFi marketGET /api/infofi/markets/:id/odds- Get market odds
GET /api/users/profile/:id- Get user profileGET /api/users/:id/raffles- Get user's raffle participationGET /api/users/:id/infofi-positions- Get user's InfoFi market positionsGET /api/users/:id/portfolio- Get user's portfolioPUT /api/users/profile/:id- Update user profile
GET /sse/raffles- SSE endpoint for real-time raffle updatesGET /sse/infofi-markets- SSE endpoint for real-time InfoFi market updatesGET /sse/arbitrage- SSE endpoint for arbitrage opportunities
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a pull request
MIT
For questions or support, please open an issue on GitHub.
This project uses Foundry for deployments via contracts/script/Deploy.s.sol:DeployScript.
- Foundry installed (
forge,anvil) - Environment variables set (see below)
Required env vars (place in your shell or a .env you source before running):
# Chainlink VRF (use realistic values for live networks)
export VRF_COORDINATOR=0x000000000000000000000000000000000000cAFe
export VRF_KEY_HASH=0x0000000000000000000000000000000000000000000000000000000000000000
export VRF_SUBSCRIPTION_ID=0
# Deployer key for testnet/mainnet (never commit!)
export PRIVATE_KEY=your_private_key
# RPC URLs
export RPC_URL_TESTNET=https://sepolia.infura.io/v3/<YOUR_KEY>
export RPC_URL_MAINNET=https://mainnet.infura.io/v3/<YOUR_KEY>
# Optional verification keys if you verify later
export ETHERSCAN_API_KEY=your_keyYou can use the provided npm scripts from the repo root.
Terminal A (start local node):
npm run anvilTerminal B (deploy to Anvil using default mnemonic):
npm run deploy:anvilOne-shot (spawns Anvil, waits, deploys):
npm run anvil:deployAlternatively, using a private key:
export ANVIL_PK=<anvil_account_private_key>
npm run deploy:anvil:pkExample (Sepolia shown; replace with your target):
cd contracts
forge script script/Deploy.s.sol:DeployScript \
--rpc-url "$RPC_URL_TESTNET" \
--private-key "$PRIVATE_KEY" \
--broadcast -vvvvIf your deploy script reads VRF env vars, ensure VRF_COORDINATOR, VRF_KEY_HASH, and VRF_SUBSCRIPTION_ID are set for the target chain.
Optional verification (if configured in your script):
forge verify-contract --chain sepolia <DEPLOYED_ADDRESS> <FULLY_QUALIFIED_NAME> \
--etherscan-api-key "$ETHERSCAN_API_KEY"Same flow as testnet, but point to a mainnet RPC. Double-check VRF config and balances.
cd contracts
forge script script/Deploy.s.sol:DeployScript \
--rpc-url "$RPC_URL_MAINNET" \
--private-key "$PRIVATE_KEY" \
--broadcast -vvvvBest practices:
- Fund the deployer with adequate ETH for gas.
- Use a hardware wallet or secure key management for
PRIVATE_KEY. - Review
Deploy.s.solparameters before broadcasting.