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.
Files changed (304) hide show
  1. package/.claude/settings.local.json +280 -0
  2. package/CLAUDE.md +46 -0
  3. package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
  4. package/CURRENT_SESSION.md +171 -0
  5. package/CURRENT_SESSION_DRAW.md +516 -0
  6. package/MARCH_MADNESS_SURVIVOR.md +254 -0
  7. package/PANDA.md +166 -0
  8. package/Procfile +4 -0
  9. package/README.md +476 -0
  10. package/controllers/livescoresController.js +376 -0
  11. package/controllers/pickemController.js +554 -0
  12. package/controllers/survivorAdminController.js +887 -0
  13. package/controllers/survivorController.js +623 -0
  14. package/cron/oracleMonitor.js +77 -0
  15. package/cron/pickemOracleMonitor.js +73 -0
  16. package/data/jackpot-history.json +952 -0
  17. package/data/ncaaTeams.js +406 -0
  18. package/documentation/API_SECURITY_GUIDE.md +327 -0
  19. package/documentation/ARCADE_API.md +593 -0
  20. package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
  21. package/documentation/ARCADE_QUICKSTART.md +242 -0
  22. package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
  23. package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
  24. package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
  25. package/documentation/CLAIM_STATUS_FIX.md +67 -0
  26. package/documentation/CLI_TOOL_GUIDE.md +372 -0
  27. package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
  28. package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
  29. package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
  30. package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
  31. package/documentation/DATABASE_QUERIES.md +269 -0
  32. package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
  33. package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
  34. package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
  35. package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
  36. package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
  37. package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
  38. package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
  39. package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
  40. package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
  41. package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
  42. package/documentation/HEROKU_DEPLOYMENT.md +134 -0
  43. package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
  44. package/documentation/JACKPOT_API.md +521 -0
  45. package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
  46. package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
  47. package/documentation/JWT_QUICK_SETUP.md +268 -0
  48. package/documentation/JWT_TESTING_GUIDE.md +404 -0
  49. package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
  50. package/documentation/KEEPER_SETUP.md +206 -0
  51. package/documentation/KEEPER_STATE_MACHINE.md +423 -0
  52. package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
  53. package/documentation/LOCAL_VOTING_TEST.md +279 -0
  54. package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
  55. package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
  56. package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
  57. package/documentation/PNL_TRACKING_SETUP.md +189 -0
  58. package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
  59. package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
  60. package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
  61. package/documentation/QUICK_AUTH_SETUP.md +99 -0
  62. package/documentation/QUICK_DEPLOY.md +224 -0
  63. package/documentation/QUICK_FIX.md +114 -0
  64. package/documentation/QUICK_START.md +152 -0
  65. package/documentation/REFEREE_MODE_GUIDE.md +392 -0
  66. package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
  67. package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
  68. package/documentation/RUN_MIGRATION_NOW.md +39 -0
  69. package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
  70. package/documentation/SETUP_GUIDE.md +184 -0
  71. package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
  72. package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
  73. package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
  74. package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
  75. package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
  76. package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
  77. package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
  78. package/ecosystem.config.js +65 -0
  79. package/env.template +125 -0
  80. package/middleware/apiKeyAuth.js +136 -0
  81. package/middleware/authenticate.js +214 -0
  82. package/middleware/developerUserAuth.js +76 -0
  83. package/middleware/socketAuth.js +69 -0
  84. package/package.json +49 -0
  85. package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
  86. package/postman/Dubs-API-v1.postman_collection.json +205 -0
  87. package/postman/Dubs_Developer_API.postman_collection.json +662 -0
  88. package/postman/QUICKSTART.md +118 -0
  89. package/postman/QUICK_REFERENCE.md +246 -0
  90. package/postman/README.md +71 -0
  91. package/postman/VOTING_API_GUIDE.md +426 -0
  92. package/refactor/Animations.md +148 -0
  93. package/refactor/Chat.md +252 -0
  94. package/routes/actionsRoutes.js +699 -0
  95. package/routes/adminRoutes.js +370 -0
  96. package/routes/analyticsRoutes.js +1262 -0
  97. package/routes/arcadeRoutes.js +557 -0
  98. package/routes/authRoutes.js +2310 -0
  99. package/routes/avatarRoutes.js +85 -0
  100. package/routes/botRoutes.js +211 -0
  101. package/routes/chatRoutes.js +377 -0
  102. package/routes/cryptoPriceRoutes.js +105 -0
  103. package/routes/developerRoutes.js +4201 -0
  104. package/routes/deviceRoutes.js +214 -0
  105. package/routes/dmRoutes.js +167 -0
  106. package/routes/esportsRoutes.js +806 -0
  107. package/routes/exchangeRateRoutes.js +233 -0
  108. package/routes/gamesRoutes.js +3028 -0
  109. package/routes/jackpotRoutes.js +754 -0
  110. package/routes/keeperMonitoringRoutes.js +156 -0
  111. package/routes/keeperWebhookRoutes.js +466 -0
  112. package/routes/livescoresRoutes.js +31 -0
  113. package/routes/pickemAdminRoutes.js +199 -0
  114. package/routes/pickemRoutes.js +231 -0
  115. package/routes/playerStatsRoutes.js +147 -0
  116. package/routes/portfolioRoutes.js +217 -0
  117. package/routes/promoRoutes.js +418 -0
  118. package/routes/referralEarningsRoutes.js +392 -0
  119. package/routes/socialRoutes.js +459 -0
  120. package/routes/sportsRoutes.js +1271 -0
  121. package/routes/survivorAdminRoutes.js +345 -0
  122. package/routes/survivorRoutes.js +756 -0
  123. package/routes/uploadRoutes.js +256 -0
  124. package/routes/userProfileRoutes.js +244 -0
  125. package/routes/whatsNewRoutes.js +331 -0
  126. package/scripts/.claude/settings.local.json +15 -0
  127. package/scripts/README.md +170 -0
  128. package/scripts/RESTART_EVERYTHING.sh +104 -0
  129. package/scripts/add-claim-columns.sql +48 -0
  130. package/scripts/add-crypto-prices-cache.sql +27 -0
  131. package/scripts/add-exchange-rates-cache.sql +40 -0
  132. package/scripts/add-game-invite-column.sql +23 -0
  133. package/scripts/add-game-invite-notification.sql +33 -0
  134. package/scripts/add-game-invite-telegram-pref.sql +16 -0
  135. package/scripts/add-game-joined-notification.sql +16 -0
  136. package/scripts/add-game-joined-pref.js +40 -0
  137. package/scripts/add-game-joined-preference.sql +6 -0
  138. package/scripts/add-game-start-notifications.sql +41 -0
  139. package/scripts/add-notification-flags-to-games.sql +55 -0
  140. package/scripts/add-pending-game-dismissals.sql +19 -0
  141. package/scripts/add-preferred-currency.sql +34 -0
  142. package/scripts/add-winner-columns.js +61 -0
  143. package/scripts/add_mention_system.sql +53 -0
  144. package/scripts/add_payment_system.sql +96 -0
  145. package/scripts/add_sports_event_id_column.sql +22 -0
  146. package/scripts/analyze-cohort-data-heroku.js +276 -0
  147. package/scripts/analyze-cohort-data.js +295 -0
  148. package/scripts/analyze-prod-cohorts.sh +10 -0
  149. package/scripts/backfill-matchup-images.js +245 -0
  150. package/scripts/backfill-missing-signatures.js +175 -0
  151. package/scripts/backfill-referral-earnings.js +202 -0
  152. package/scripts/check-chat-schema.js +130 -0
  153. package/scripts/check-db.sh +14 -0
  154. package/scripts/check_oracle_in_game.js +54 -0
  155. package/scripts/cleanup-database.js +193 -0
  156. package/scripts/clear-notification-cache.js +85 -0
  157. package/scripts/convert-mnemonic.js +50 -0
  158. package/scripts/create-users-table.sql +44 -0
  159. package/scripts/debug-cohort-counts.js +248 -0
  160. package/scripts/debug-winner-calc.js +84 -0
  161. package/scripts/deploy-payment-system.sh +118 -0
  162. package/scripts/deploy-to-heroku.sh +63 -0
  163. package/scripts/diagnose-locked-round.js +143 -0
  164. package/scripts/dubs-cli.js +720 -0
  165. package/scripts/dump-account.js +65 -0
  166. package/scripts/find-vrf-offset.js +48 -0
  167. package/scripts/fix-chat-notifications-constraint.sql +122 -0
  168. package/scripts/fix-claim-columns.js +124 -0
  169. package/scripts/fix-constraint-now.js +44 -0
  170. package/scripts/fix-lock-timestamps.js +96 -0
  171. package/scripts/fix-locked-round.sh +126 -0
  172. package/scripts/fix-missing-badges.sql +91 -0
  173. package/scripts/fix-payment-notifications.sql +41 -0
  174. package/scripts/force-new-round.js +55 -0
  175. package/scripts/force-resolve-and-claim.js +278 -0
  176. package/scripts/important/README.md +115 -0
  177. package/scripts/important/authority-force-lock.js +197 -0
  178. package/scripts/important/authority-resolve-game.js +267 -0
  179. package/scripts/important/check-game-status.js +373 -0
  180. package/scripts/important/list-pending-games-by-version.js +270 -0
  181. package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
  182. package/scripts/initialize-jackpot.js +111 -0
  183. package/scripts/jackpot/.claude/settings.local.json +10 -0
  184. package/scripts/jackpot/force-reset.js +84 -0
  185. package/scripts/jackpot/initialize-mainnet.js +100 -0
  186. package/scripts/jackpot/keeper.js +742 -0
  187. package/scripts/jackpot/status.js +107 -0
  188. package/scripts/jackpot/update-round-duration.js +143 -0
  189. package/scripts/keeper-bot.js +112 -0
  190. package/scripts/list-pending-games.js +131 -0
  191. package/scripts/migrate-chat-v2.js +127 -0
  192. package/scripts/migrate-chat-winners.js +84 -0
  193. package/scripts/migrate-chat.sh +17 -0
  194. package/scripts/migrate-game-invite.js +83 -0
  195. package/scripts/migrate-heroku-game-notifications.sh +159 -0
  196. package/scripts/migrations/001_analytics_tables.sql +422 -0
  197. package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
  198. package/scripts/migrations/003_referral_earnings.sql +208 -0
  199. package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
  200. package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
  201. package/scripts/migrations/005_push_notifications.sql +55 -0
  202. package/scripts/migrations/006_add_draw_team_players.sql +28 -0
  203. package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
  204. package/scripts/migrations/007_add_gif_url.sql +8 -0
  205. package/scripts/migrations/008_add_connect4_columns.sql +139 -0
  206. package/scripts/migrations/008_add_pool_tracking.sql +22 -0
  207. package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
  208. package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
  209. package/scripts/migrations/011_create_developer_tables.sql +67 -0
  210. package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
  211. package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
  212. package/scripts/migrations/013_add_network_mode.sql +18 -0
  213. package/scripts/migrations/014_create_developer_app_users.sql +19 -0
  214. package/scripts/migrations/015_add_ui_config.sql +4 -0
  215. package/scripts/migrations/016_add_resolution_secret.sql +4 -0
  216. package/scripts/migrations/017_add_external_game_id.sql +3 -0
  217. package/scripts/migrations/018_create_pickem_tables.sql +115 -0
  218. package/scripts/migrations/019_expo_push_tokens.sql +19 -0
  219. package/scripts/migrations/create_whats_new_tables.sql +88 -0
  220. package/scripts/migrations/drop_live_games_tables.sql +34 -0
  221. package/scripts/open-jackpot-round.js +85 -0
  222. package/scripts/purge-all-data.sh +329 -0
  223. package/scripts/purge-all-data.sql +142 -0
  224. package/scripts/purge-heroku-data.sh +149 -0
  225. package/scripts/purge-heroku-data.sql +62 -0
  226. package/scripts/rebuild-heroku-database.sh +113 -0
  227. package/scripts/recover-funds.js +357 -0
  228. package/scripts/regenerate-epl-images.js +278 -0
  229. package/scripts/resize-s3-matchup-images.js +374 -0
  230. package/scripts/resolve-direct.js +88 -0
  231. package/scripts/resolve-mock-game.js +124 -0
  232. package/scripts/resolve-pickem-game.js +55 -0
  233. package/scripts/resolve-round-manual.js +83 -0
  234. package/scripts/resolve-stuck-game.js +382 -0
  235. package/scripts/resolve-stuck-round.js +42 -0
  236. package/scripts/run-connect4-migration.sh +16 -0
  237. package/scripts/run-mention-migration.sh +32 -0
  238. package/scripts/run-payment-migration.sh +51 -0
  239. package/scripts/run-preferred-currency-migration.sh +31 -0
  240. package/scripts/run-referral-earnings-migration.sh +32 -0
  241. package/scripts/run-survivor-outcome-migration.sh +16 -0
  242. package/scripts/seed-test-users.js +346 -0
  243. package/scripts/setup-auth-tables.js +78 -0
  244. package/scripts/setup-complete-database.sql +992 -0
  245. package/scripts/setup-database-fresh.sh +359 -0
  246. package/scripts/setup-heroku-keeper.sh +48 -0
  247. package/scripts/setup-keeper-database.js +83 -0
  248. package/scripts/setup-keeper-state-db.sql +110 -0
  249. package/scripts/setup-oracle.sh +39 -0
  250. package/scripts/setup-pnl-tracking.js +111 -0
  251. package/scripts/start-devnet.sh +14 -0
  252. package/scripts/test-arcade-devnet.sh +160 -0
  253. package/scripts/test-arcade-match.sh +109 -0
  254. package/scripts/test-automatic-mode.sh +239 -0
  255. package/scripts/test-connect4-cancel-claim.js +370 -0
  256. package/scripts/test-connect4-e2e.js +369 -0
  257. package/scripts/test-connect4-resolve.js +369 -0
  258. package/scripts/test-game-state-endpoint.js +136 -0
  259. package/scripts/test-invite-notification.js +86 -0
  260. package/scripts/test-jackpot-api.sh +71 -0
  261. package/scripts/test-poll-confirmation.js +267 -0
  262. package/scripts/test-resolve-game.js +271 -0
  263. package/scripts/test-resolve-signature.js +223 -0
  264. package/scripts/test-signature-preservation.js +124 -0
  265. package/scripts/test-state-machine.js +291 -0
  266. package/scripts/test-webhook-receiver.js +60 -0
  267. package/scripts/update-notification-constraint.js +52 -0
  268. package/scripts/verify-account-layout.js +145 -0
  269. package/scripts/verify-winner-algorithm.js +278 -0
  270. package/server.js +5259 -0
  271. package/services/arcadeMatchService.js +763 -0
  272. package/services/automaticGameOracle.js +1596 -0
  273. package/services/chatService.js +1612 -0
  274. package/services/connect4GameService.js +1049 -0
  275. package/services/connect4NotificationService.js +374 -0
  276. package/services/cryptoPriceService.js +223 -0
  277. package/services/customGameResolver.js +260 -0
  278. package/services/db.js +79 -0
  279. package/services/directMessageService.js +389 -0
  280. package/services/discordNotifications.js +160 -0
  281. package/services/exchangeRateService.js +289 -0
  282. package/services/expoPushService.js +314 -0
  283. package/services/gamesCacheService.js +539 -0
  284. package/services/jackpotHistory.js +331 -0
  285. package/services/jackpotService.js +856 -0
  286. package/services/keeperStateService.js +355 -0
  287. package/services/matchupImageService.js +591 -0
  288. package/services/notificationCacheService.js +407 -0
  289. package/services/pickemOracle.js +440 -0
  290. package/services/playerStatsService.js +389 -0
  291. package/services/portfolioService.js +555 -0
  292. package/services/promoService.js +757 -0
  293. package/services/promoTreasuryService.js +239 -0
  294. package/services/pushNotifications.js +353 -0
  295. package/services/redisService.js +422 -0
  296. package/services/referralEarningsService.js +728 -0
  297. package/services/s3Service.js +396 -0
  298. package/services/socialService.js +1202 -0
  299. package/services/survivorOracle.js +469 -0
  300. package/services/survivorSimulator.js +475 -0
  301. package/services/telegramNotifications.js +461 -0
  302. package/services/userProfileStatsService.js +1185 -0
  303. package/services/whatsNewService.js +388 -0
  304. 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
+