dubs-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +280 -0
- package/CLAUDE.md +46 -0
- package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
- package/CURRENT_SESSION.md +171 -0
- package/CURRENT_SESSION_DRAW.md +516 -0
- package/MARCH_MADNESS_SURVIVOR.md +254 -0
- package/PANDA.md +166 -0
- package/Procfile +4 -0
- package/README.md +476 -0
- package/controllers/livescoresController.js +376 -0
- package/controllers/pickemController.js +554 -0
- package/controllers/survivorAdminController.js +887 -0
- package/controllers/survivorController.js +623 -0
- package/cron/oracleMonitor.js +77 -0
- package/cron/pickemOracleMonitor.js +73 -0
- package/data/jackpot-history.json +952 -0
- package/data/ncaaTeams.js +406 -0
- package/documentation/API_SECURITY_GUIDE.md +327 -0
- package/documentation/ARCADE_API.md +593 -0
- package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
- package/documentation/ARCADE_QUICKSTART.md +242 -0
- package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
- package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
- package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
- package/documentation/CLAIM_STATUS_FIX.md +67 -0
- package/documentation/CLI_TOOL_GUIDE.md +372 -0
- package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
- package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
- package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
- package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
- package/documentation/DATABASE_QUERIES.md +269 -0
- package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
- package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
- package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
- package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
- package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
- package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
- package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
- package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
- package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
- package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
- package/documentation/HEROKU_DEPLOYMENT.md +134 -0
- package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
- package/documentation/JACKPOT_API.md +521 -0
- package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
- package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
- package/documentation/JWT_QUICK_SETUP.md +268 -0
- package/documentation/JWT_TESTING_GUIDE.md +404 -0
- package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
- package/documentation/KEEPER_SETUP.md +206 -0
- package/documentation/KEEPER_STATE_MACHINE.md +423 -0
- package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
- package/documentation/LOCAL_VOTING_TEST.md +279 -0
- package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
- package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
- package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
- package/documentation/PNL_TRACKING_SETUP.md +189 -0
- package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
- package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
- package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
- package/documentation/QUICK_AUTH_SETUP.md +99 -0
- package/documentation/QUICK_DEPLOY.md +224 -0
- package/documentation/QUICK_FIX.md +114 -0
- package/documentation/QUICK_START.md +152 -0
- package/documentation/REFEREE_MODE_GUIDE.md +392 -0
- package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
- package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
- package/documentation/RUN_MIGRATION_NOW.md +39 -0
- package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
- package/documentation/SETUP_GUIDE.md +184 -0
- package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
- package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
- package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
- package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
- package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
- package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
- package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
- package/ecosystem.config.js +65 -0
- package/env.template +125 -0
- package/middleware/apiKeyAuth.js +136 -0
- package/middleware/authenticate.js +214 -0
- package/middleware/developerUserAuth.js +76 -0
- package/middleware/socketAuth.js +69 -0
- package/package.json +49 -0
- package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
- package/postman/Dubs-API-v1.postman_collection.json +205 -0
- package/postman/Dubs_Developer_API.postman_collection.json +662 -0
- package/postman/QUICKSTART.md +118 -0
- package/postman/QUICK_REFERENCE.md +246 -0
- package/postman/README.md +71 -0
- package/postman/VOTING_API_GUIDE.md +426 -0
- package/refactor/Animations.md +148 -0
- package/refactor/Chat.md +252 -0
- package/routes/actionsRoutes.js +699 -0
- package/routes/adminRoutes.js +370 -0
- package/routes/analyticsRoutes.js +1262 -0
- package/routes/arcadeRoutes.js +557 -0
- package/routes/authRoutes.js +2310 -0
- package/routes/avatarRoutes.js +85 -0
- package/routes/botRoutes.js +211 -0
- package/routes/chatRoutes.js +377 -0
- package/routes/cryptoPriceRoutes.js +105 -0
- package/routes/developerRoutes.js +4201 -0
- package/routes/deviceRoutes.js +214 -0
- package/routes/dmRoutes.js +167 -0
- package/routes/esportsRoutes.js +806 -0
- package/routes/exchangeRateRoutes.js +233 -0
- package/routes/gamesRoutes.js +3028 -0
- package/routes/jackpotRoutes.js +754 -0
- package/routes/keeperMonitoringRoutes.js +156 -0
- package/routes/keeperWebhookRoutes.js +466 -0
- package/routes/livescoresRoutes.js +31 -0
- package/routes/pickemAdminRoutes.js +199 -0
- package/routes/pickemRoutes.js +231 -0
- package/routes/playerStatsRoutes.js +147 -0
- package/routes/portfolioRoutes.js +217 -0
- package/routes/promoRoutes.js +418 -0
- package/routes/referralEarningsRoutes.js +392 -0
- package/routes/socialRoutes.js +459 -0
- package/routes/sportsRoutes.js +1271 -0
- package/routes/survivorAdminRoutes.js +345 -0
- package/routes/survivorRoutes.js +756 -0
- package/routes/uploadRoutes.js +256 -0
- package/routes/userProfileRoutes.js +244 -0
- package/routes/whatsNewRoutes.js +331 -0
- package/scripts/.claude/settings.local.json +15 -0
- package/scripts/README.md +170 -0
- package/scripts/RESTART_EVERYTHING.sh +104 -0
- package/scripts/add-claim-columns.sql +48 -0
- package/scripts/add-crypto-prices-cache.sql +27 -0
- package/scripts/add-exchange-rates-cache.sql +40 -0
- package/scripts/add-game-invite-column.sql +23 -0
- package/scripts/add-game-invite-notification.sql +33 -0
- package/scripts/add-game-invite-telegram-pref.sql +16 -0
- package/scripts/add-game-joined-notification.sql +16 -0
- package/scripts/add-game-joined-pref.js +40 -0
- package/scripts/add-game-joined-preference.sql +6 -0
- package/scripts/add-game-start-notifications.sql +41 -0
- package/scripts/add-notification-flags-to-games.sql +55 -0
- package/scripts/add-pending-game-dismissals.sql +19 -0
- package/scripts/add-preferred-currency.sql +34 -0
- package/scripts/add-winner-columns.js +61 -0
- package/scripts/add_mention_system.sql +53 -0
- package/scripts/add_payment_system.sql +96 -0
- package/scripts/add_sports_event_id_column.sql +22 -0
- package/scripts/analyze-cohort-data-heroku.js +276 -0
- package/scripts/analyze-cohort-data.js +295 -0
- package/scripts/analyze-prod-cohorts.sh +10 -0
- package/scripts/backfill-matchup-images.js +245 -0
- package/scripts/backfill-missing-signatures.js +175 -0
- package/scripts/backfill-referral-earnings.js +202 -0
- package/scripts/check-chat-schema.js +130 -0
- package/scripts/check-db.sh +14 -0
- package/scripts/check_oracle_in_game.js +54 -0
- package/scripts/cleanup-database.js +193 -0
- package/scripts/clear-notification-cache.js +85 -0
- package/scripts/convert-mnemonic.js +50 -0
- package/scripts/create-users-table.sql +44 -0
- package/scripts/debug-cohort-counts.js +248 -0
- package/scripts/debug-winner-calc.js +84 -0
- package/scripts/deploy-payment-system.sh +118 -0
- package/scripts/deploy-to-heroku.sh +63 -0
- package/scripts/diagnose-locked-round.js +143 -0
- package/scripts/dubs-cli.js +720 -0
- package/scripts/dump-account.js +65 -0
- package/scripts/find-vrf-offset.js +48 -0
- package/scripts/fix-chat-notifications-constraint.sql +122 -0
- package/scripts/fix-claim-columns.js +124 -0
- package/scripts/fix-constraint-now.js +44 -0
- package/scripts/fix-lock-timestamps.js +96 -0
- package/scripts/fix-locked-round.sh +126 -0
- package/scripts/fix-missing-badges.sql +91 -0
- package/scripts/fix-payment-notifications.sql +41 -0
- package/scripts/force-new-round.js +55 -0
- package/scripts/force-resolve-and-claim.js +278 -0
- package/scripts/important/README.md +115 -0
- package/scripts/important/authority-force-lock.js +197 -0
- package/scripts/important/authority-resolve-game.js +267 -0
- package/scripts/important/check-game-status.js +373 -0
- package/scripts/important/list-pending-games-by-version.js +270 -0
- package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
- package/scripts/initialize-jackpot.js +111 -0
- package/scripts/jackpot/.claude/settings.local.json +10 -0
- package/scripts/jackpot/force-reset.js +84 -0
- package/scripts/jackpot/initialize-mainnet.js +100 -0
- package/scripts/jackpot/keeper.js +742 -0
- package/scripts/jackpot/status.js +107 -0
- package/scripts/jackpot/update-round-duration.js +143 -0
- package/scripts/keeper-bot.js +112 -0
- package/scripts/list-pending-games.js +131 -0
- package/scripts/migrate-chat-v2.js +127 -0
- package/scripts/migrate-chat-winners.js +84 -0
- package/scripts/migrate-chat.sh +17 -0
- package/scripts/migrate-game-invite.js +83 -0
- package/scripts/migrate-heroku-game-notifications.sh +159 -0
- package/scripts/migrations/001_analytics_tables.sql +422 -0
- package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
- package/scripts/migrations/003_referral_earnings.sql +208 -0
- package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
- package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
- package/scripts/migrations/005_push_notifications.sql +55 -0
- package/scripts/migrations/006_add_draw_team_players.sql +28 -0
- package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
- package/scripts/migrations/007_add_gif_url.sql +8 -0
- package/scripts/migrations/008_add_connect4_columns.sql +139 -0
- package/scripts/migrations/008_add_pool_tracking.sql +22 -0
- package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
- package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
- package/scripts/migrations/011_create_developer_tables.sql +67 -0
- package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
- package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
- package/scripts/migrations/013_add_network_mode.sql +18 -0
- package/scripts/migrations/014_create_developer_app_users.sql +19 -0
- package/scripts/migrations/015_add_ui_config.sql +4 -0
- package/scripts/migrations/016_add_resolution_secret.sql +4 -0
- package/scripts/migrations/017_add_external_game_id.sql +3 -0
- package/scripts/migrations/018_create_pickem_tables.sql +115 -0
- package/scripts/migrations/019_expo_push_tokens.sql +19 -0
- package/scripts/migrations/create_whats_new_tables.sql +88 -0
- package/scripts/migrations/drop_live_games_tables.sql +34 -0
- package/scripts/open-jackpot-round.js +85 -0
- package/scripts/purge-all-data.sh +329 -0
- package/scripts/purge-all-data.sql +142 -0
- package/scripts/purge-heroku-data.sh +149 -0
- package/scripts/purge-heroku-data.sql +62 -0
- package/scripts/rebuild-heroku-database.sh +113 -0
- package/scripts/recover-funds.js +357 -0
- package/scripts/regenerate-epl-images.js +278 -0
- package/scripts/resize-s3-matchup-images.js +374 -0
- package/scripts/resolve-direct.js +88 -0
- package/scripts/resolve-mock-game.js +124 -0
- package/scripts/resolve-pickem-game.js +55 -0
- package/scripts/resolve-round-manual.js +83 -0
- package/scripts/resolve-stuck-game.js +382 -0
- package/scripts/resolve-stuck-round.js +42 -0
- package/scripts/run-connect4-migration.sh +16 -0
- package/scripts/run-mention-migration.sh +32 -0
- package/scripts/run-payment-migration.sh +51 -0
- package/scripts/run-preferred-currency-migration.sh +31 -0
- package/scripts/run-referral-earnings-migration.sh +32 -0
- package/scripts/run-survivor-outcome-migration.sh +16 -0
- package/scripts/seed-test-users.js +346 -0
- package/scripts/setup-auth-tables.js +78 -0
- package/scripts/setup-complete-database.sql +992 -0
- package/scripts/setup-database-fresh.sh +359 -0
- package/scripts/setup-heroku-keeper.sh +48 -0
- package/scripts/setup-keeper-database.js +83 -0
- package/scripts/setup-keeper-state-db.sql +110 -0
- package/scripts/setup-oracle.sh +39 -0
- package/scripts/setup-pnl-tracking.js +111 -0
- package/scripts/start-devnet.sh +14 -0
- package/scripts/test-arcade-devnet.sh +160 -0
- package/scripts/test-arcade-match.sh +109 -0
- package/scripts/test-automatic-mode.sh +239 -0
- package/scripts/test-connect4-cancel-claim.js +370 -0
- package/scripts/test-connect4-e2e.js +369 -0
- package/scripts/test-connect4-resolve.js +369 -0
- package/scripts/test-game-state-endpoint.js +136 -0
- package/scripts/test-invite-notification.js +86 -0
- package/scripts/test-jackpot-api.sh +71 -0
- package/scripts/test-poll-confirmation.js +267 -0
- package/scripts/test-resolve-game.js +271 -0
- package/scripts/test-resolve-signature.js +223 -0
- package/scripts/test-signature-preservation.js +124 -0
- package/scripts/test-state-machine.js +291 -0
- package/scripts/test-webhook-receiver.js +60 -0
- package/scripts/update-notification-constraint.js +52 -0
- package/scripts/verify-account-layout.js +145 -0
- package/scripts/verify-winner-algorithm.js +278 -0
- package/server.js +5259 -0
- package/services/arcadeMatchService.js +763 -0
- package/services/automaticGameOracle.js +1596 -0
- package/services/chatService.js +1612 -0
- package/services/connect4GameService.js +1049 -0
- package/services/connect4NotificationService.js +374 -0
- package/services/cryptoPriceService.js +223 -0
- package/services/customGameResolver.js +260 -0
- package/services/db.js +79 -0
- package/services/directMessageService.js +389 -0
- package/services/discordNotifications.js +160 -0
- package/services/exchangeRateService.js +289 -0
- package/services/expoPushService.js +314 -0
- package/services/gamesCacheService.js +539 -0
- package/services/jackpotHistory.js +331 -0
- package/services/jackpotService.js +856 -0
- package/services/keeperStateService.js +355 -0
- package/services/matchupImageService.js +591 -0
- package/services/notificationCacheService.js +407 -0
- package/services/pickemOracle.js +440 -0
- package/services/playerStatsService.js +389 -0
- package/services/portfolioService.js +555 -0
- package/services/promoService.js +757 -0
- package/services/promoTreasuryService.js +239 -0
- package/services/pushNotifications.js +353 -0
- package/services/redisService.js +422 -0
- package/services/referralEarningsService.js +728 -0
- package/services/s3Service.js +396 -0
- package/services/socialService.js +1202 -0
- package/services/survivorOracle.js +469 -0
- package/services/survivorSimulator.js +475 -0
- package/services/telegramNotifications.js +461 -0
- package/services/userProfileStatsService.js +1185 -0
- package/services/whatsNewService.js +388 -0
- package/utils/urlHelper.js +95 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# 📊 PNL Tracking Setup Guide
|
|
2
|
+
|
|
3
|
+
Complete setup for player PNL tracking with epic winner announcements!
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Setup (One Command)
|
|
6
|
+
|
|
7
|
+
Run this on your Heroku app to set up EVERYTHING:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
heroku run node scripts/setup-pnl-tracking.js --app YOUR_APP_NAME
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This will:
|
|
14
|
+
- ✅ Add winner announcement columns to `chat_messages`
|
|
15
|
+
- ✅ Create `player_stats` table
|
|
16
|
+
- ✅ Create `player_history` table for charts
|
|
17
|
+
- ✅ Create all necessary indexes
|
|
18
|
+
|
|
19
|
+
## 📦 What Gets Created
|
|
20
|
+
|
|
21
|
+
### 1. Chat Messages (Winner Announcements)
|
|
22
|
+
```sql
|
|
23
|
+
ALTER TABLE chat_messages ADD:
|
|
24
|
+
- is_winner_announcement BOOLEAN
|
|
25
|
+
- win_amount NUMERIC(20, 9)
|
|
26
|
+
- round_id INTEGER
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. Player Stats Table
|
|
30
|
+
```sql
|
|
31
|
+
CREATE TABLE player_stats (
|
|
32
|
+
wallet_address VARCHAR(100) PRIMARY KEY,
|
|
33
|
+
total_wagered NUMERIC(20, 9),
|
|
34
|
+
total_won NUMERIC(20, 9),
|
|
35
|
+
net_pnl NUMERIC(20, 9),
|
|
36
|
+
rounds_played INTEGER,
|
|
37
|
+
rounds_won INTEGER,
|
|
38
|
+
biggest_win NUMERIC(20, 9),
|
|
39
|
+
biggest_win_round INTEGER,
|
|
40
|
+
last_played TIMESTAMP,
|
|
41
|
+
created_at TIMESTAMP
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Player History Table (For Charts)
|
|
46
|
+
```sql
|
|
47
|
+
CREATE TABLE player_history (
|
|
48
|
+
id SERIAL PRIMARY KEY,
|
|
49
|
+
wallet_address VARCHAR(100),
|
|
50
|
+
round_id INTEGER,
|
|
51
|
+
action VARCHAR(20), -- 'entry' or 'win'
|
|
52
|
+
amount NUMERIC(20, 9),
|
|
53
|
+
cumulative_pnl NUMERIC(20, 9),
|
|
54
|
+
timestamp TIMESTAMP
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 🔌 API Endpoints
|
|
59
|
+
|
|
60
|
+
### Get Player Stats
|
|
61
|
+
```http
|
|
62
|
+
GET /stats/player/:walletAddress
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Response:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"success": true,
|
|
69
|
+
"stats": {
|
|
70
|
+
"walletAddress": "...",
|
|
71
|
+
"totalWagered": 12.5,
|
|
72
|
+
"totalWon": 15.3,
|
|
73
|
+
"netPNL": 2.8,
|
|
74
|
+
"roundsPlayed": 10,
|
|
75
|
+
"roundsWon": 2,
|
|
76
|
+
"winRate": "20.00",
|
|
77
|
+
"biggestWin": 8.5,
|
|
78
|
+
"biggestWinRound": 42
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Get Player History (Chart Data)
|
|
84
|
+
```http
|
|
85
|
+
GET /stats/player/:walletAddress/history?limit=100
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Get Leaderboard
|
|
89
|
+
```http
|
|
90
|
+
GET /stats/leaderboard?limit=10
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🎯 Automatic Tracking
|
|
94
|
+
|
|
95
|
+
### Player Entries
|
|
96
|
+
When a player bets, the frontend automatically calls:
|
|
97
|
+
```javascript
|
|
98
|
+
POST /api/keeper-webhook/player-entry
|
|
99
|
+
{
|
|
100
|
+
walletAddress,
|
|
101
|
+
roundId,
|
|
102
|
+
amount // in lamports
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Player Wins
|
|
107
|
+
When a winner is selected, the keeper automatically calls:
|
|
108
|
+
```javascript
|
|
109
|
+
POST /api/keeper-webhook/winner-selected
|
|
110
|
+
{
|
|
111
|
+
roundId,
|
|
112
|
+
winner,
|
|
113
|
+
winAmount, // in lamports
|
|
114
|
+
totalPot,
|
|
115
|
+
entryCount
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This will:
|
|
120
|
+
1. 📊 Track the win in player_stats
|
|
121
|
+
2. 🏆 Post epic winner announcement to chat
|
|
122
|
+
3. 📢 Broadcast to all WebSocket clients
|
|
123
|
+
|
|
124
|
+
## 🎨 Frontend Features
|
|
125
|
+
|
|
126
|
+
### PNL Modal
|
|
127
|
+
Click any player name to see:
|
|
128
|
+
- Total wagered
|
|
129
|
+
- Total won
|
|
130
|
+
- Net PNL (profit/loss)
|
|
131
|
+
- Win rate
|
|
132
|
+
- Rounds played/won
|
|
133
|
+
- Biggest win
|
|
134
|
+
- **Animated chart** showing cumulative PNL over time
|
|
135
|
+
|
|
136
|
+
### Epic Winner Announcements
|
|
137
|
+
When someone wins, the chat shows:
|
|
138
|
+
- 🔥 Laser beams sweeping across
|
|
139
|
+
- 💥 Explosive particles
|
|
140
|
+
- 🏆 Bouncing trophy
|
|
141
|
+
- 🌈 Rainbow gradient text
|
|
142
|
+
- ✨ Floating emojis
|
|
143
|
+
- All with Tron-style effects!
|
|
144
|
+
|
|
145
|
+
## 🧪 Testing
|
|
146
|
+
|
|
147
|
+
### 1. Check Database
|
|
148
|
+
```bash
|
|
149
|
+
heroku pg:psql --app YOUR_APP_NAME -c "SELECT * FROM player_stats LIMIT 5;"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2. Test API
|
|
153
|
+
```bash
|
|
154
|
+
curl https://your-app.herokuapp.com/stats/player/WALLET_ADDRESS
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 3. Check Logs
|
|
158
|
+
```bash
|
|
159
|
+
heroku logs --tail --app YOUR_APP_NAME
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Look for:
|
|
163
|
+
- `📊 Entry recorded`
|
|
164
|
+
- `🏆 Win recorded`
|
|
165
|
+
- `🔥 EPIC WINNER ANNOUNCEMENT posted to chat!`
|
|
166
|
+
|
|
167
|
+
## 🔄 After Setup
|
|
168
|
+
|
|
169
|
+
1. **Restart the server:**
|
|
170
|
+
```bash
|
|
171
|
+
heroku restart --app YOUR_APP_NAME
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
2. **Play a round** - Stats will start tracking automatically!
|
|
175
|
+
|
|
176
|
+
3. **Click on any player name** in chat or roster to see their PNL modal
|
|
177
|
+
|
|
178
|
+
## 🎉 Done!
|
|
179
|
+
|
|
180
|
+
Your app now has:
|
|
181
|
+
- ✅ Full PNL tracking
|
|
182
|
+
- ✅ Beautiful animated charts
|
|
183
|
+
- ✅ Epic winner announcements with lasers
|
|
184
|
+
- ✅ Clickable player stats
|
|
185
|
+
- ✅ Leaderboards
|
|
186
|
+
- ✅ Pixelify Sans font everywhere! 🎮
|
|
187
|
+
|
|
188
|
+
EVERYTHING is wired up and ready to go! 🚀
|
|
189
|
+
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# 🛡️ Preventing Jackpot Lockup Errors
|
|
2
|
+
|
|
3
|
+
**Lessons learned from Round 50 lockup incident**
|
|
4
|
+
|
|
5
|
+
## What Went Wrong
|
|
6
|
+
|
|
7
|
+
### Primary Issues
|
|
8
|
+
1. **Winner calculation bug**: Hardcoded `entries[0].player` instead of calculating from VRF
|
|
9
|
+
2. **Wrong offset**: VRF result read from offset 87 instead of actual offset 51
|
|
10
|
+
3. **No automated tests**: Bugs weren't caught before production
|
|
11
|
+
4. **Keeper bot not running**: Manual intervention required
|
|
12
|
+
5. **No monitoring**: Issue wasn't detected automatically
|
|
13
|
+
|
|
14
|
+
## Prevention Strategy
|
|
15
|
+
|
|
16
|
+
### 1. Automated Testing
|
|
17
|
+
|
|
18
|
+
#### Unit Tests for Winner Calculation
|
|
19
|
+
```javascript
|
|
20
|
+
// tests/winner-calculation.test.js
|
|
21
|
+
const { calculateWinner } = require('../services/winnerCalculator');
|
|
22
|
+
|
|
23
|
+
describe('Winner Calculation', () => {
|
|
24
|
+
it('should calculate winner based on VRF result and cumulative weights', () => {
|
|
25
|
+
const entries = [
|
|
26
|
+
{ player: 'A', weight: 10, cumulativeTo: 10 },
|
|
27
|
+
{ player: 'B', weight: 20, cumulativeTo: 30 },
|
|
28
|
+
{ player: 'C', weight: 20, cumulativeTo: 50 },
|
|
29
|
+
];
|
|
30
|
+
const vrfResult = 25n;
|
|
31
|
+
const totalWeight = 50n;
|
|
32
|
+
|
|
33
|
+
const winner = calculateWinner(vrfResult, totalWeight, entries);
|
|
34
|
+
expect(winner).toBe('B'); // 25 falls in B's range (10-30)
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle edge case at boundary', () => {
|
|
38
|
+
const entries = [
|
|
39
|
+
{ player: 'A', weight: 10, cumulativeTo: 10 },
|
|
40
|
+
{ player: 'B', weight: 20, cumulativeTo: 30 },
|
|
41
|
+
];
|
|
42
|
+
const vrfResult = 10n;
|
|
43
|
+
const totalWeight = 30n;
|
|
44
|
+
|
|
45
|
+
const winner = calculateWinner(vrfResult, totalWeight, entries);
|
|
46
|
+
expect(winner).toBe('B'); // 10 is NOT less than 10, check next
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Integration Tests with Live Program
|
|
52
|
+
```javascript
|
|
53
|
+
// tests/jackpot-integration.test.js
|
|
54
|
+
describe('Jackpot Integration Tests', () => {
|
|
55
|
+
it('should complete full round lifecycle', async () => {
|
|
56
|
+
// 1. Open round
|
|
57
|
+
// 2. Add entries
|
|
58
|
+
// 3. Lock round
|
|
59
|
+
// 4. Reveal randomness
|
|
60
|
+
// 5. Resolve round
|
|
61
|
+
// 6. Verify winner matches calculation
|
|
62
|
+
// 7. Verify payout amounts
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should match on-chain and off-chain winner calculation', async () => {
|
|
66
|
+
// Read VRF result from chain
|
|
67
|
+
// Calculate winner off-chain
|
|
68
|
+
// Attempt resolve with calculated winner
|
|
69
|
+
// Should succeed
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Account Layout Tests
|
|
75
|
+
```javascript
|
|
76
|
+
// tests/account-layout.test.js
|
|
77
|
+
describe('Round Account Layout', () => {
|
|
78
|
+
it('should read all fields at correct offsets', async () => {
|
|
79
|
+
const roundAccount = await getRoundAccount();
|
|
80
|
+
|
|
81
|
+
expect(readU64(roundAccount, 8)).toBe(expectedRoundId);
|
|
82
|
+
expect(readU8(roundAccount, 16)).toBe(expectedStatus);
|
|
83
|
+
expect(readU64(roundAccount, 33)).toBe(expectedPot);
|
|
84
|
+
expect(readU64(roundAccount, 41)).toBe(expectedWeight);
|
|
85
|
+
expect(readOptionU128(roundAccount, 51)).toBe(expectedVrfResult);
|
|
86
|
+
expect(readU32(roundAccount, 104)).toBe(expectedEntryCount);
|
|
87
|
+
// etc...
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. Account Layout Documentation
|
|
93
|
+
|
|
94
|
+
Create a reference file with EXACT offsets:
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// constants/accountLayouts.js
|
|
98
|
+
const ROUND_ACCOUNT_LAYOUT = {
|
|
99
|
+
discriminator: { offset: 0, size: 8, type: '[u8; 8]' },
|
|
100
|
+
roundId: { offset: 8, size: 8, type: 'u64' },
|
|
101
|
+
status: { offset: 16, size: 1, type: 'u8' }, // 0=Open, 1=Locked, 2=Resolved
|
|
102
|
+
startSlot: { offset: 17, size: 8, type: 'u64' },
|
|
103
|
+
endSlot: { offset: 25, size: 8, type: 'u64' },
|
|
104
|
+
totalPotLamports: { offset: 33, size: 8, type: 'u64' },
|
|
105
|
+
totalWeight: { offset: 41, size: 8, type: 'u64' },
|
|
106
|
+
winnerIndex: { offset: 49, size: 5, type: 'Option<u32>' },
|
|
107
|
+
winner: { offset: 54, size: 33, type: 'Option<Pubkey>' },
|
|
108
|
+
vrfResult: { offset: 87, size: 17, type: 'Option<u128>' }, // WAIT - offset 51!
|
|
109
|
+
// ^^^ THIS WAS THE BUG! Document TESTED offsets only
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Export helper functions
|
|
113
|
+
function readVrfResult(accountData) {
|
|
114
|
+
const offset = 51; // VERIFIED through testing
|
|
115
|
+
if (accountData[offset] !== 1) return null;
|
|
116
|
+
return accountData.slice(offset + 1, offset + 17);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3. Extract Winner Calculation to Separate Module
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// services/winnerCalculator.js
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Calculate winner using weighted random selection
|
|
127
|
+
* This MUST match the on-chain calculation exactly!
|
|
128
|
+
*
|
|
129
|
+
* Algorithm (from programs/dubs_jackpot_program/src/lib.rs:283-295):
|
|
130
|
+
* 1. winner_point = vrf_result % total_weight
|
|
131
|
+
* 2. Find first entry where cumulative_to > winner_point
|
|
132
|
+
*/
|
|
133
|
+
function calculateWinner(vrfResult, totalWeight, entries) {
|
|
134
|
+
if (!vrfResult || !totalWeight || !entries || entries.length === 0) {
|
|
135
|
+
throw new Error('Invalid input for winner calculation');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const winnerPoint = vrfResult % totalWeight;
|
|
139
|
+
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
if (BigInt(entry.cumulativeTo) > winnerPoint) {
|
|
142
|
+
return entry.player;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error('Winner selection failed - no matching entry found');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = { calculateWinner };
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 4. Monitoring & Alerts
|
|
153
|
+
|
|
154
|
+
#### Health Check Script
|
|
155
|
+
```javascript
|
|
156
|
+
// scripts/health-check.js
|
|
157
|
+
|
|
158
|
+
async function checkJackpotHealth() {
|
|
159
|
+
const round = await getCurrentRound();
|
|
160
|
+
|
|
161
|
+
// Check 1: Is keeper bot running?
|
|
162
|
+
if (!isProcessRunning('jackpot-keeper')) {
|
|
163
|
+
alert('🚨 KEEPER BOT NOT RUNNING!');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check 2: Is round stuck?
|
|
167
|
+
if (round.status === 'Locked') {
|
|
168
|
+
const lockedDuration = currentSlot - round.endSlot;
|
|
169
|
+
if (lockedDuration > 300) { // 2 minutes
|
|
170
|
+
alert('🚨 ROUND STUCK IN LOCKED STATE!');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check 3: Is API responding?
|
|
175
|
+
try {
|
|
176
|
+
await fetch('http://localhost:3001/jackpot/health');
|
|
177
|
+
} catch (e) {
|
|
178
|
+
alert('🚨 API SERVER DOWN!');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check 4: Are accounts reachable?
|
|
182
|
+
const roundAccount = await connection.getAccountInfo(roundPda);
|
|
183
|
+
if (!roundAccount) {
|
|
184
|
+
alert('🚨 ROUND ACCOUNT NOT FOUND!');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Run every 30 seconds
|
|
189
|
+
setInterval(checkJackpotHealth, 30000);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Discord/Telegram Alerts
|
|
193
|
+
```javascript
|
|
194
|
+
// services/alerting.js
|
|
195
|
+
|
|
196
|
+
async function sendAlert(message, severity = 'warning') {
|
|
197
|
+
// Send to Discord
|
|
198
|
+
await axios.post(DISCORD_WEBHOOK, {
|
|
199
|
+
content: `[${severity.toUpperCase()}] ${message}`,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Send to Telegram
|
|
203
|
+
await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
|
|
204
|
+
chat_id: ADMIN_CHAT_ID,
|
|
205
|
+
text: message,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Log to file
|
|
209
|
+
fs.appendFileSync('alerts.log', `${new Date().toISOString()} [${severity}] ${message}\n`);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 5. Keeper Bot Reliability
|
|
214
|
+
|
|
215
|
+
#### Process Manager (PM2)
|
|
216
|
+
```bash
|
|
217
|
+
# Install PM2
|
|
218
|
+
npm install -g pm2
|
|
219
|
+
|
|
220
|
+
# Start keeper bot with PM2
|
|
221
|
+
pm2 start scripts/jackpot-keeper.js --name jackpot-keeper
|
|
222
|
+
|
|
223
|
+
# Auto-restart on crash
|
|
224
|
+
pm2 startup
|
|
225
|
+
pm2 save
|
|
226
|
+
|
|
227
|
+
# Monitor
|
|
228
|
+
pm2 logs jackpot-keeper
|
|
229
|
+
pm2 monit
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Keeper Bot Health Check
|
|
233
|
+
```javascript
|
|
234
|
+
// Inside jackpot-keeper.js
|
|
235
|
+
|
|
236
|
+
class JackpotKeeper {
|
|
237
|
+
constructor() {
|
|
238
|
+
// ... existing code ...
|
|
239
|
+
this.lastHeartbeat = Date.now();
|
|
240
|
+
this.startHeartbeat();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
startHeartbeat() {
|
|
244
|
+
setInterval(() => {
|
|
245
|
+
const uptime = Date.now() - this.startTime;
|
|
246
|
+
fs.writeFileSync('.keeper-heartbeat', JSON.stringify({
|
|
247
|
+
timestamp: Date.now(),
|
|
248
|
+
uptime,
|
|
249
|
+
lastRoundProcessed: this.lastRoundId,
|
|
250
|
+
}));
|
|
251
|
+
}, 5000);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### External Heartbeat Monitor
|
|
257
|
+
```javascript
|
|
258
|
+
// scripts/monitor-keeper.js
|
|
259
|
+
|
|
260
|
+
setInterval(() => {
|
|
261
|
+
const heartbeat = JSON.parse(fs.readFileSync('.keeper-heartbeat'));
|
|
262
|
+
const age = Date.now() - heartbeat.timestamp;
|
|
263
|
+
|
|
264
|
+
if (age > 30000) { // No heartbeat for 30 seconds
|
|
265
|
+
sendAlert('🚨 KEEPER BOT HEARTBEAT MISSING!', 'critical');
|
|
266
|
+
// Auto-restart
|
|
267
|
+
exec('pm2 restart jackpot-keeper');
|
|
268
|
+
}
|
|
269
|
+
}, 10000);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 6. Pre-Deployment Checklist
|
|
273
|
+
|
|
274
|
+
Create a checklist that MUST be completed before deploying changes:
|
|
275
|
+
|
|
276
|
+
```markdown
|
|
277
|
+
## Deployment Checklist
|
|
278
|
+
|
|
279
|
+
### Code Quality
|
|
280
|
+
- [ ] All unit tests pass (`npm test`)
|
|
281
|
+
- [ ] Integration tests pass (`npm run test:integration`)
|
|
282
|
+
- [ ] Linter passes with no errors (`npm run lint`)
|
|
283
|
+
- [ ] No hardcoded values (check for TODO, FIXME, TEMP)
|
|
284
|
+
- [ ] Winner calculation matches smart contract logic
|
|
285
|
+
|
|
286
|
+
### Account Layout
|
|
287
|
+
- [ ] All offsets documented in `accountLayouts.js`
|
|
288
|
+
- [ ] Offsets verified with `scripts/verify-account-layout.js`
|
|
289
|
+
- [ ] VRF result offset tested on devnet
|
|
290
|
+
|
|
291
|
+
### Testing
|
|
292
|
+
- [ ] Tested full round lifecycle on devnet
|
|
293
|
+
- [ ] Verified winner selection with 3+ entries
|
|
294
|
+
- [ ] Tested edge cases (1 entry, max entries, ties)
|
|
295
|
+
- [ ] Confirmed keeper bot can process locked rounds
|
|
296
|
+
|
|
297
|
+
### Monitoring
|
|
298
|
+
- [ ] Health check script running
|
|
299
|
+
- [ ] Alerts configured and tested
|
|
300
|
+
- [ ] PM2 configured for auto-restart
|
|
301
|
+
- [ ] Logs being collected
|
|
302
|
+
|
|
303
|
+
### Documentation
|
|
304
|
+
- [ ] CHANGELOG.md updated
|
|
305
|
+
- [ ] API docs updated if endpoints changed
|
|
306
|
+
- [ ] README updated if setup changed
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 7. Development Practices
|
|
310
|
+
|
|
311
|
+
#### Code Review Requirements
|
|
312
|
+
```markdown
|
|
313
|
+
## Code Review Checklist
|
|
314
|
+
|
|
315
|
+
For any changes to jackpot logic:
|
|
316
|
+
|
|
317
|
+
1. **Algorithm Verification**
|
|
318
|
+
- Does off-chain match on-chain? (Cite line numbers in Rust code)
|
|
319
|
+
- Are there unit tests?
|
|
320
|
+
- Has it been tested on devnet?
|
|
321
|
+
|
|
322
|
+
2. **Account Parsing**
|
|
323
|
+
- Are offsets documented?
|
|
324
|
+
- Are they verified with tests?
|
|
325
|
+
- Do they match the Rust struct layout?
|
|
326
|
+
|
|
327
|
+
3. **Error Handling**
|
|
328
|
+
- What happens if VRF result is missing?
|
|
329
|
+
- What happens if entries array is empty?
|
|
330
|
+
- What happens if keeper bot crashes mid-round?
|
|
331
|
+
|
|
332
|
+
4. **Testing**
|
|
333
|
+
- Manual testing completed?
|
|
334
|
+
- Automated tests added?
|
|
335
|
+
- Edge cases covered?
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Git Hooks (Pre-commit)
|
|
339
|
+
```bash
|
|
340
|
+
#!/bin/bash
|
|
341
|
+
# .git/hooks/pre-commit
|
|
342
|
+
|
|
343
|
+
echo "Running pre-commit checks..."
|
|
344
|
+
|
|
345
|
+
# Run tests
|
|
346
|
+
npm test || {
|
|
347
|
+
echo "❌ Tests failed! Fix before committing."
|
|
348
|
+
exit 1
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# Run linter
|
|
352
|
+
npm run lint || {
|
|
353
|
+
echo "❌ Linter failed! Fix before committing."
|
|
354
|
+
exit 1
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
# Check for dangerous patterns
|
|
358
|
+
if git diff --cached | grep -E "entries\[0\]\.player|FIXME|TODO.*urgent"; then
|
|
359
|
+
echo "⚠️ Warning: Suspicious patterns found. Review carefully!"
|
|
360
|
+
echo " - Hardcoded entries[0]?"
|
|
361
|
+
echo " - Urgent TODOs?"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
echo "✅ Pre-commit checks passed!"
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### 8. Testnet-First Development
|
|
368
|
+
|
|
369
|
+
```markdown
|
|
370
|
+
## Development Workflow
|
|
371
|
+
|
|
372
|
+
1. **Make changes on devnet**
|
|
373
|
+
- Deploy to devnet first
|
|
374
|
+
- Run for 24 hours
|
|
375
|
+
- Monitor for issues
|
|
376
|
+
|
|
377
|
+
2. **Stress testing**
|
|
378
|
+
- Process 50+ rounds
|
|
379
|
+
- Test with 1, 5, 10, 50 entries
|
|
380
|
+
- Test edge cases (no entries, time boundaries)
|
|
381
|
+
|
|
382
|
+
3. **Mainnet deployment**
|
|
383
|
+
- Only deploy after 24h on devnet
|
|
384
|
+
- Monitor closely for first 10 rounds
|
|
385
|
+
- Have rollback plan ready
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### 9. Circuit Breakers
|
|
389
|
+
|
|
390
|
+
Add automatic pause functionality:
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
// In jackpot-keeper.js
|
|
394
|
+
|
|
395
|
+
async function processRound(round) {
|
|
396
|
+
// Circuit breaker: Stop if repeated failures
|
|
397
|
+
if (this.consecutiveFailures > 5) {
|
|
398
|
+
console.error('🚨 TOO MANY FAILURES - STOPPING KEEPER BOT');
|
|
399
|
+
sendAlert('Keeper bot stopped due to repeated failures', 'critical');
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
// ... normal processing ...
|
|
405
|
+
this.consecutiveFailures = 0;
|
|
406
|
+
} catch (error) {
|
|
407
|
+
this.consecutiveFailures++;
|
|
408
|
+
console.error(`❌ Failure ${this.consecutiveFailures}/5:`, error.message);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### 10. Documentation Standards
|
|
414
|
+
|
|
415
|
+
Every critical function must have:
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
/**
|
|
419
|
+
* Calculate winner using weighted random selection
|
|
420
|
+
*
|
|
421
|
+
* @algorithm Matches on-chain logic from programs/dubs_jackpot_program/src/lib.rs:283-295
|
|
422
|
+
*
|
|
423
|
+
* @param {BigInt} vrfResult - Random value from VRF oracle (u128)
|
|
424
|
+
* @param {BigInt} totalWeight - Sum of all entry weights (u64)
|
|
425
|
+
* @param {Array<Entry>} entries - Array of entries with cumulative weights
|
|
426
|
+
* @returns {string} Winner's public key
|
|
427
|
+
*
|
|
428
|
+
* @tested tests/winner-calculation.test.js:15-45
|
|
429
|
+
* @verified 2024-11-15 on devnet round 50
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* const winner = calculateWinner(123456n, 50n, entries);
|
|
433
|
+
* // => 'FWUJCthDfPcgmTvdQWM5uofxxiYjqJFMMwiLYvS7LBFa'
|
|
434
|
+
*/
|
|
435
|
+
function calculateWinner(vrfResult, totalWeight, entries) {
|
|
436
|
+
// ...
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Quick Reference
|
|
441
|
+
|
|
442
|
+
### When Adding New Features
|
|
443
|
+
1. Write tests FIRST
|
|
444
|
+
2. Implement feature
|
|
445
|
+
3. Test on devnet for 24h
|
|
446
|
+
4. Get code review
|
|
447
|
+
5. Update documentation
|
|
448
|
+
6. Deploy to mainnet
|
|
449
|
+
|
|
450
|
+
### When Debugging Production Issues
|
|
451
|
+
1. Check keeper bot logs: `pm2 logs jackpot-keeper`
|
|
452
|
+
2. Check round status: `curl http://localhost:3001/jackpot/round/current`
|
|
453
|
+
3. Check account data: `node scripts/dump-account.js`
|
|
454
|
+
4. Verify winner calculation: `node scripts/debug-winner-calc.js`
|
|
455
|
+
5. Manual resolve if needed: `node scripts/resolve-direct.js`
|
|
456
|
+
|
|
457
|
+
### Emergency Contacts
|
|
458
|
+
- Developer: [Your contact]
|
|
459
|
+
- Devops: [Your contact]
|
|
460
|
+
- Alerts: check Discord #jackpot-alerts
|
|
461
|
+
|
|
462
|
+
## Conclusion
|
|
463
|
+
|
|
464
|
+
The Round 50 incident taught us that:
|
|
465
|
+
1. **Testing is not optional** - Unit and integration tests catch bugs before production
|
|
466
|
+
2. **Documentation must match reality** - Offsets must be verified, not assumed
|
|
467
|
+
3. **Monitoring is critical** - Automated alerts prevent long outages
|
|
468
|
+
4. **Keeper bot reliability is key** - Use process managers and health checks
|
|
469
|
+
5. **Code reviews save money** - Peer review catches logic errors
|
|
470
|
+
|
|
471
|
+
By following these practices, we can ensure the jackpot runs smoothly 24/7.
|
|
472
|
+
|