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,321 @@
1
+ # Automatic Sports Mode - Oracle System
2
+
3
+ ## Overview
4
+ The oracle system monitors automatic sports betting games and resolves them based on real-world game outcomes.
5
+
6
+ ## Architecture
7
+
8
+ ```
9
+ ┌─────────────────────┐
10
+ │ Oracle Monitor │ Every 5 minutes
11
+ │ (cron/oracle │ ─────────────────▶
12
+ │ Monitor.js) │
13
+ └─────────────────────┘
14
+
15
+
16
+ ┌─────────────────────┐
17
+ │ Fetch Pending │ GET /api/auth/games/automatic/pending
18
+ │ Games │ ◀──────────────────
19
+ └─────────────────────┘ dubs-games-api
20
+
21
+
22
+ ┌─────────────────────┐
23
+ │ Check Live Scores │ GET /api/livescores/:league
24
+ │ for Each Game │ ◀──────────────────
25
+ └─────────────────────┘ dubs-api
26
+
27
+
28
+ ┌─────────────────────┐
29
+ │ If Game is FINAL: │
30
+ │ - Determine Winner │
31
+ │ - Resolve on-chain │ resolve_automatic_game instruction
32
+ │ - Update Firebase │ POST /api/games/:id/resolve
33
+ └─────────────────────┘
34
+
35
+
36
+ ┌─────────────────────┐
37
+ │ Winners Claim │ claim_automatic_winnings instruction
38
+ │ Their Payouts │ (called by players)
39
+ └─────────────────────┘
40
+ ```
41
+
42
+ ## Setup
43
+
44
+ ### 1. Create Oracle Wallet
45
+
46
+ ```bash
47
+ npm run oracle:setup
48
+ ```
49
+
50
+ This creates an oracle wallet at `wallets/oracle.json` and funds it on devnet.
51
+
52
+ **Save the Oracle Public Key** - you'll need it when creating automatic games!
53
+
54
+ ### 2. Configure Environment
55
+
56
+ Copy `env.template` to `.env`:
57
+
58
+ ```bash
59
+ cp env.template .env
60
+ ```
61
+
62
+ Update values:
63
+
64
+ ```bash
65
+ # For local development
66
+ SOLANA_NETWORK=http://127.0.0.1:8899
67
+ LIVE_SCORES_API_URL=http://localhost:3000
68
+ DUBS_GAMES_API_URL=http://localhost:3001
69
+
70
+ # For production
71
+ SOLANA_NETWORK=https://api.mainnet-beta.solana.com
72
+ LIVE_SCORES_API_URL=https://dubs-api.herokuapp.com
73
+ DUBS_GAMES_API_URL=https://dubs-games-api.herokuapp.com
74
+ ```
75
+
76
+ ### 3. Start the Oracle
77
+
78
+ ```bash
79
+ # Production mode
80
+ npm run oracle:start
81
+
82
+ # Development mode (auto-restart on changes)
83
+ npm run oracle:dev
84
+ ```
85
+
86
+ The oracle will:
87
+ - ✅ Check every 5 minutes (configurable)
88
+ - ✅ Find games past their lock time
89
+ - ✅ Query live scores API
90
+ - ✅ Resolve games when sports events finish
91
+ - ✅ Update Firebase with results
92
+
93
+ ## How It Works
94
+
95
+ ### Game Lifecycle
96
+
97
+ ```
98
+ 1. User Creates Automatic Game
99
+ - Picks upcoming sports event (e.g., Lakers vs Celtics at 7:00 PM)
100
+ - Sets buy-in amount
101
+ - Oracle wallet address is set
102
+ - Lock time = 6:50 PM (10 min before tip-off)
103
+
104
+ 2. Players Join & Pick Teams
105
+ - Alice picks Lakers (home) - 0.5 SOL
106
+ - Bob picks Celtics (away) - 0.5 SOL
107
+ - Carol picks Lakers (home) - 0.5 SOL
108
+ - Total pot: 1.5 SOL
109
+
110
+ 3. Lock Time Arrives (6:50 PM)
111
+ - Game auto-locks
112
+ - No more joins allowed
113
+
114
+ 4. Real Game Happens (7:00 PM - 9:30 PM)
115
+ - Lakers vs Celtics plays out
116
+ - Oracle monitor checks every 5 minutes
117
+
118
+ 5. Game Finishes (9:30 PM)
119
+ - Oracle detects: Lakers won 110-105
120
+ - Calls resolve_automatic_game on-chain
121
+ - Sets winning_team = Home
122
+ - Updates Firebase
123
+
124
+ 6. Winners Claim Payouts
125
+ - Alice calls claim_automatic_winnings → Gets 0.75 SOL
126
+ - Carol calls claim_automatic_winnings → Gets 0.75 SOL
127
+ - Bob gets nothing (picked wrong team)
128
+ ```
129
+
130
+ ### Score Matching Logic
131
+
132
+ The oracle uses fuzzy team name matching:
133
+
134
+ ```javascript
135
+ normalizeTeamName("Los Angeles Lakers") → "lakers"
136
+ normalizeTeamName("LA Lakers") → "lakers"
137
+ normalizeTeamName("Lakers") → "lakers"
138
+ ```
139
+
140
+ This ensures games are matched even if team names differ slightly between APIs.
141
+
142
+ ## Files
143
+
144
+ | File | Purpose |
145
+ |------|---------|
146
+ | `services/automaticGameOracle.js` | Oracle service class |
147
+ | `cron/oracleMonitor.js` | Cron job runner |
148
+ | `setup-oracle.sh` | Oracle wallet setup script |
149
+ | `env.template` | Environment variables template |
150
+
151
+ ## API Integration
152
+
153
+ ### dubs-games-api Endpoints Used:
154
+
155
+ ```
156
+ GET /api/auth/games/automatic/pending
157
+ POST /api/games/:gameId/resolve
158
+ ```
159
+
160
+ ### dubs-api Endpoints Used:
161
+
162
+ ```
163
+ GET /api/livescores/:league
164
+ ```
165
+
166
+ Returns:
167
+ ```json
168
+ [
169
+ {
170
+ "date": "2025-10-27",
171
+ "game": "Lakers vs Celtics",
172
+ "status": "Final",
173
+ "competitors": [
174
+ { "name": "Los Angeles Lakers", "homeAway": "home", "score": 110 },
175
+ { "name": "Boston Celtics", "homeAway": "away", "score": 105 }
176
+ ]
177
+ }
178
+ ]
179
+ ```
180
+
181
+ ## Monitoring
182
+
183
+ ### Check Oracle Status
184
+
185
+ ```bash
186
+ # Check if oracle process is running
187
+ ps aux | grep oracleMonitor
188
+
189
+ # View logs
190
+ tail -f /path/to/your/logs
191
+ ```
192
+
193
+ ### Manual Resolution (if needed)
194
+
195
+ If oracle misses a game, you can manually trigger resolution:
196
+
197
+ ```bash
198
+ curl -X POST http://localhost:3001/api/games/adam-amy-001/resolve \
199
+ -H "Content-Type: application/json" \
200
+ -d '{
201
+ "winner": "home",
202
+ "homeScore": 105,
203
+ "awayScore": 98,
204
+ "resolvedBy": "manual"
205
+ }'
206
+ ```
207
+
208
+ ## Security
209
+
210
+ ### Oracle Authorization
211
+
212
+ - Only the designated oracle wallet can resolve games
213
+ - Validated on-chain: `require!(oracle.key() == game.oracle)`
214
+ - Oracle private key must be kept secure
215
+
216
+ ### Best Practices
217
+
218
+ 1. **Secure the Oracle Wallet**
219
+ - Keep `wallets/oracle.json` in `.gitignore`
220
+ - Use environment variables in production
221
+ - Consider hardware wallet for mainnet
222
+
223
+ 2. **Monitor Oracle Health**
224
+ - Set up alerts if oracle stops
225
+ - Log all resolution attempts
226
+ - Have backup oracle wallet ready
227
+
228
+ 3. **Handle Edge Cases**
229
+ - Postponed games → Manual resolution
230
+ - Cancelled games → Refund all players
231
+ - API downtime → Retry logic
232
+
233
+ ## Testing
234
+
235
+ ### Test the Oracle Locally
236
+
237
+ 1. Start local validator:
238
+ ```bash
239
+ solana-test-validator --reset
240
+ ```
241
+
242
+ 2. Deploy program:
243
+ ```bash
244
+ anchor deploy
245
+ ```
246
+
247
+ 3. Setup oracle:
248
+ ```bash
249
+ npm run oracle:setup
250
+ ```
251
+
252
+ 4. Create test game (with mock event that's already finished):
253
+ ```bash
254
+ curl -X POST http://localhost:3001/api/auth/games/save \
255
+ -d '{ "gameId": "test-001", "sportsEvent": {...} }'
256
+ ```
257
+
258
+ 5. Start oracle:
259
+ ```bash
260
+ npm run oracle:dev
261
+ ```
262
+
263
+ 6. Watch logs - oracle should detect and resolve the game!
264
+
265
+ ## Production Deployment
266
+
267
+ ### Heroku
268
+
269
+ Add to `Procfile`:
270
+ ```
271
+ web: node server.js
272
+ oracle: node cron/oracleMonitor.js
273
+ ```
274
+
275
+ Then scale:
276
+ ```bash
277
+ heroku ps:scale web=1 oracle=1
278
+ ```
279
+
280
+ ### Environment Variables
281
+
282
+ Set in Heroku:
283
+ ```bash
284
+ heroku config:set ORACLE_WALLET_PATH=/app/wallets/oracle.json
285
+ heroku config:set LIVE_SCORES_API_URL=https://dubs-api.herokuapp.com
286
+ heroku config:set DUBS_GAMES_API_URL=https://dubs-games-api.herokuapp.com
287
+ ```
288
+
289
+ ## Troubleshooting
290
+
291
+ ### Oracle not resolving games
292
+
293
+ 1. Check oracle is running: `ps aux | grep oracle`
294
+ 2. Check API connectivity: `curl $LIVE_SCORES_API_URL/api/livescores/NBA`
295
+ 3. Check pending games exist: `curl $DUBS_GAMES_API_URL/api/auth/games/automatic/pending`
296
+ 4. Check oracle wallet has SOL for transactions
297
+
298
+ ### Games resolved with wrong winner
299
+
300
+ 1. Check live scores API response format
301
+ 2. Verify team name normalization logic
302
+ 3. Check date matching (timezone issues?)
303
+
304
+ ### Transactions failing
305
+
306
+ 1. Oracle wallet needs SOL for transaction fees
307
+ 2. Check program ID is correct
308
+ 3. Verify game exists on-chain
309
+
310
+ ## Future Enhancements
311
+
312
+ - [ ] Multi-oracle consensus (2-of-3 oracles must agree)
313
+ - [ ] Automatic refunds for postponed/cancelled games
314
+ - [ ] Discord/Telegram notifications when games resolve
315
+ - [ ] Dashboard for monitoring oracle health
316
+ - [ ] Automatic oracle wallet top-up
317
+
318
+ ---
319
+
320
+ **Phase 3: Oracle System** ✅ Complete!
321
+
@@ -0,0 +1,171 @@
1
+ # 🐛 Bug Fix: Cohort Date Display - "Nov 30, 2025" Issue
2
+
3
+ ## Problem Identified
4
+
5
+ **User Report:**
6
+ > "How can we have more than 20 users but only 7 signups - this data makes no sense"
7
+
8
+ **Screenshot showed:**
9
+ - Cohort labeled: "December 2025" with subtitle "Nov 30, 2025"
10
+ - 7 signups in that cohort
11
+ - Data seemed inconsistent
12
+
13
+ ## Root Cause Analysis
14
+
15
+ ### Investigation Results (dubs-server-dev):
16
+
17
+ ```
18
+ Total Unique Users: 7
19
+ Total registration_completed Events: 11
20
+ Date Range: Dec 14, 2025 to Dec 31, 2025
21
+ ```
22
+
23
+ **All 7 users signed up in DECEMBER**, but the label showed "Nov 30, 2025"!
24
+
25
+ ### The Bug
26
+
27
+ When PostgreSQL returns monthly cohort data:
28
+ ```sql
29
+ DATE_TRUNC('month', '2025-12-15')
30
+ -- Returns: 2025-12-01T00:00:00.000Z (December 1st, UTC)
31
+ ```
32
+
33
+ JavaScript's `toLocaleDateString()` without timezone specification:
34
+ ```javascript
35
+ new Date('2025-12-01T00:00:00.000Z').toLocaleDateString('en-US')
36
+ // In PST timezone: "Nov 30, 2025" ❌ (off by one day!)
37
+ // Should be: "December 2025" ✅
38
+ ```
39
+
40
+ **Why it happens:**
41
+ - PostgreSQL stores dates in UTC
42
+ - `DATE_TRUNC('month')` returns midnight UTC on the 1st of the month
43
+ - JavaScript `toLocaleDateString()` converts to local timezone
44
+ - In Pacific timezone (UTC-8), midnight UTC on Dec 1 = 4pm Nov 30
45
+ - Result: **November 30 displayed instead of December!**
46
+
47
+ ## The Fix
48
+
49
+ ### Backend (routes/analyticsRoutes.js)
50
+
51
+ **Before:**
52
+ ```javascript
53
+ dateRange = cohortDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
54
+ ```
55
+
56
+ **After:**
57
+ ```javascript
58
+ dateRange = cohortDate.toLocaleDateString('en-US', {
59
+ month: 'long',
60
+ year: 'numeric',
61
+ timeZone: 'UTC' // ✅ Force UTC interpretation
62
+ });
63
+ ```
64
+
65
+ ### Frontend (AnalyticsDashboard.tsx)
66
+
67
+ **Before:**
68
+ ```javascript
69
+ {new Date(cohort.signup_cohort).toLocaleDateString('en-US', {
70
+ month: 'short', day: 'numeric', year: 'numeric'
71
+ })}
72
+ ```
73
+
74
+ **After:**
75
+ ```javascript
76
+ {new Date(cohort.signup_cohort).toLocaleDateString('en-US', {
77
+ month: 'short', day: 'numeric', year: 'numeric',
78
+ timeZone: 'UTC' // ✅ Force UTC interpretation
79
+ })}
80
+ ```
81
+
82
+ ## Files Changed
83
+
84
+ 1. **Backend:** `routes/analyticsRoutes.js`
85
+ - Fixed `GET /api/analytics/cohort-retention` date formatting (line ~997-1000)
86
+ - Fixed `GET /api/analytics/cohort-retention/csv` date formatting (line ~1166-1172)
87
+
88
+ 2. **Frontend:** `app/v2/components/overlay/AnalyticsDashboard.tsx`
89
+ - Fixed cohort subtitle date display (line ~1160)
90
+
91
+ ## Testing
92
+
93
+ ### Before Fix (Dev Database):
94
+ ```
95
+ Cohort: December 2025
96
+ Subtitle: Nov 30, 2025 ❌ WRONG
97
+ Signups: 7
98
+ ```
99
+
100
+ ### After Fix (Expected):
101
+ ```
102
+ Cohort: December 2025
103
+ Subtitle: Dec 1, 2025 ✅ CORRECT
104
+ Signups: 7
105
+ ```
106
+
107
+ ## Verification Steps
108
+
109
+ 1. **Deploy fixes to dev:**
110
+ ```bash
111
+ cd /Users/adamdahan/Developer/iheartsolana/solana-programs/dubs-server
112
+ git add routes/analyticsRoutes.js
113
+ git commit -m "fix: Cohort date display timezone bug"
114
+ git push heroku-dev main
115
+ ```
116
+
117
+ 2. **Refresh dashboard:**
118
+ - Open dubs-jackpot-spa in dev
119
+ - Navigate to Analytics Dashboard
120
+ - Expand "Cohort Retention Analysis"
121
+ - Verify "December 2025" shows correct date
122
+
123
+ 3. **Test CSV export:**
124
+ - Click "Export CSV"
125
+ - Open file
126
+ - Verify dates are correct
127
+
128
+ ## Impact
129
+
130
+ **Before:**
131
+ - ❌ Confusing "Nov 30, 2025" label for December cohort
132
+ - ❌ Off-by-one-day errors in weekly cohorts
133
+ - ❌ Users questioning data accuracy
134
+
135
+ **After:**
136
+ - ✅ Correct month/year labels
137
+ - ✅ Accurate date ranges
138
+ - ✅ Clear, trustworthy data presentation
139
+
140
+ ## Related Issues
141
+
142
+ This timezone bug affected:
143
+ 1. Monthly cohort labels (most visible)
144
+ 2. Weekly cohort date ranges
145
+ 3. CSV export headers
146
+ 4. Frontend subtitle displays
147
+
148
+ All fixed with `timeZone: 'UTC'` parameter!
149
+
150
+ ## Lessons Learned
151
+
152
+ 1. **Always specify timezone** when formatting dates from database
153
+ 2. **PostgreSQL returns UTC** - JavaScript must interpret as UTC
154
+ 3. **Test across timezones** - PST/EST/UTC can show different dates
155
+ 4. **Use UTC for cohort logic** - Business logic should be timezone-agnostic
156
+
157
+ ## Prevention
158
+
159
+ Add to code review checklist:
160
+ - [ ] All `toLocaleDateString()` calls have `timeZone` specified
161
+ - [ ] Date formatting uses UTC for database timestamps
162
+ - [ ] Test in different timezones (PST, EST, UTC)
163
+
164
+ ---
165
+
166
+ **Status:** ✅ FIXED
167
+ **Deployed:** Pending (commit ready)
168
+ **Impact:** High (user-facing data display)
169
+ **Complexity:** Low (timezone parameter addition)
170
+
171
+
@@ -0,0 +1,52 @@
1
+ # Add Claim Columns Migration
2
+
3
+ ## Problem
4
+ The claim endpoint is failing with:
5
+ ```
6
+ ERROR: column "claimed_at" of relation "user_game_refs" does not exist
7
+ ```
8
+
9
+ ## Solution
10
+ Run this migration to add claim tracking columns to the `user_game_refs` table.
11
+
12
+ ## Run on Heroku:
13
+
14
+ ```bash
15
+ # Option 1: Via Heroku CLI
16
+ heroku pg:psql --app dubs-server-dev < scripts/add-claim-columns.sql
17
+
18
+ # Option 2: Interactive
19
+ heroku pg:psql --app dubs-server-dev
20
+ # Then paste the SQL from add-claim-columns.sql
21
+ ```
22
+
23
+ ## What It Adds:
24
+
25
+ | Column | Type | Purpose |
26
+ |--------|------|---------|
27
+ | `claimed_at` | TIMESTAMP | When user claimed prize |
28
+ | `claim_signature` | TEXT | Solana transaction signature |
29
+ | `claim_explorer_url` | TEXT | Explorer link for claim tx |
30
+ | `amount_claimed` | DECIMAL(20,9) | Amount of SOL claimed |
31
+
32
+ ## Verify:
33
+
34
+ After running, you should see:
35
+ ```
36
+ NOTICE: Added claimed_at column
37
+ NOTICE: Added claim_signature column
38
+ NOTICE: Added claim_explorer_url column
39
+ NOTICE: Added amount_claimed column
40
+
41
+ column_name | data_type
42
+ ---------------------+-----------
43
+ amount_claimed | numeric
44
+ claim_explorer_url | text
45
+ claim_signature | text
46
+ claimed_at | timestamp
47
+ ```
48
+
49
+ ## Test:
50
+
51
+ After migration, try claiming a prize again. It should work!
52
+
@@ -0,0 +1,67 @@
1
+ # Claim Status Fix - Deployed ✅
2
+
3
+ ## Problem
4
+ The MyGamesWidget was showing already-claimed games as still "Claimable". When users tried to claim, they got the error: "You have already claimed your winnings for this game"
5
+
6
+ ## Root Cause
7
+ The backend API endpoint `GET /api/auth/games/user/:walletAddress` was **not returning** the `claimedAt` field, even though it existed in the database. This meant the frontend had no way to detect if a game had already been claimed.
8
+
9
+ ## What Was Fixed
10
+
11
+ ### 1. Backend API Response (routes/gamesRoutes.js)
12
+ Added claim-related fields to the API response:
13
+ - `claimedAt` - Timestamp when user claimed
14
+ - `claimSignature` - Solana transaction signature
15
+ - `claimExplorerUrl` - Explorer link for the claim transaction
16
+ - `amountClaimed` - Amount of SOL claimed
17
+
18
+ ### 2. Table Schema (routes/gamesRoutes.js)
19
+ Updated the `user_game_refs` table creation to include claim columns by default:
20
+ - `claimed_at TIMESTAMP`
21
+ - `claim_signature TEXT`
22
+ - `claim_explorer_url TEXT`
23
+ - `amount_claimed DECIMAL(20, 9)`
24
+ - `updated_at TIMESTAMP DEFAULT NOW()`
25
+
26
+ ### 3. Migration Script (scripts/fix-claim-columns.js)
27
+ Created a migration script to add these columns to existing databases.
28
+
29
+ ## Database Status
30
+ ✅ All claim columns already exist in the Heroku database (they were added previously)
31
+ ✅ Verified columns with correct data types
32
+
33
+ ## Deployment
34
+ - **Committed**: 4e8f22e
35
+ - **Deployed to Heroku**: v179
36
+ - **Pushed to GitHub**: ✅
37
+
38
+ ## How It Works Now
39
+
40
+ 1. User views their games in MyGamesWidget
41
+ 2. API returns `claimedAt` field for each game
42
+ 3. Frontend checks `game.claimedAt` at line 109:
43
+ ```typescript
44
+ if (game.claimedAt) {
45
+ return 'claimed';
46
+ }
47
+ ```
48
+ 4. Already-claimed games show blue "✓ Claimed" status
49
+ 5. Only truly claimable games show green "💰 Claimable" status
50
+
51
+ ## Testing
52
+ To test with your wallet `3sgQXvLKs4XfBTxG3jY2J2E5NougemfC6i4nZbUVx8xt`:
53
+
54
+ **Expected Results:**
55
+ - `sport-1764428545106-jeet2jl9s`: Shows as "Claimed" (claimed on Nov 29)
56
+ - `sport-1764428315170-5bq7g8tqe`: Shows as "Claimable" (not claimed yet)
57
+ - Other games show appropriate status based on win/loss
58
+
59
+ ## Next Steps
60
+ Just refresh your dubs-jackpot app - the API is now returning the claim status correctly!
61
+
62
+
63
+
64
+
65
+
66
+
67
+