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,91 @@
1
+ -- Retroactively award badges to users who have referrals but no badges
2
+
3
+ -- Get users who have referrals
4
+ WITH referral_counts AS (
5
+ SELECT
6
+ u.id as user_id,
7
+ u.my_referral_code,
8
+ COUNT(referred.id) as referral_count
9
+ FROM users u
10
+ LEFT JOIN users referred ON referred.referral_code = u.my_referral_code
11
+ WHERE u.my_referral_code IS NOT NULL
12
+ GROUP BY u.id, u.my_referral_code
13
+ )
14
+ -- Award badges based on thresholds
15
+ INSERT INTO user_badges (user_id, badge_type, badge_name, badge_description, badge_icon, referral_count)
16
+ SELECT
17
+ rc.user_id,
18
+ CASE
19
+ WHEN rc.referral_count >= 10 THEN 'captain'
20
+ WHEN rc.referral_count >= 5 THEN 'ambassador'
21
+ WHEN rc.referral_count >= 1 THEN 'recruiter'
22
+ END as badge_type,
23
+ CASE
24
+ WHEN rc.referral_count >= 10 THEN 'Captain'
25
+ WHEN rc.referral_count >= 5 THEN 'Ambassador'
26
+ WHEN rc.referral_count >= 1 THEN 'Recruiter'
27
+ END as badge_name,
28
+ CASE
29
+ WHEN rc.referral_count >= 10 THEN 'Referred 10 or more users'
30
+ WHEN rc.referral_count >= 5 THEN 'Referred 5 users'
31
+ WHEN rc.referral_count >= 1 THEN 'Referred your first user'
32
+ END as badge_description,
33
+ CASE
34
+ WHEN rc.referral_count >= 10 THEN '/badges/badge_2-removebg-preview.png'
35
+ WHEN rc.referral_count >= 5 THEN '/badges/badge_1-removebg-preview.png'
36
+ WHEN rc.referral_count >= 1 THEN '/badges/badge_0-removebg-preview.png'
37
+ END as badge_icon,
38
+ rc.referral_count
39
+ FROM referral_counts rc
40
+ WHERE rc.referral_count >= 1
41
+ ON CONFLICT (user_id, badge_type) DO UPDATE
42
+ SET referral_count = EXCLUDED.referral_count;
43
+
44
+ -- Award lower tier badges if user has higher tier
45
+ INSERT INTO user_badges (user_id, badge_type, badge_name, badge_description, badge_icon, referral_count)
46
+ SELECT DISTINCT
47
+ ub.user_id,
48
+ 'recruiter',
49
+ 'Recruiter',
50
+ 'Referred your first user',
51
+ '/badges/badge_0-removebg-preview.png',
52
+ ub.referral_count
53
+ FROM user_badges ub
54
+ WHERE ub.badge_type IN ('ambassador', 'captain')
55
+ AND NOT EXISTS (
56
+ SELECT 1 FROM user_badges ub2
57
+ WHERE ub2.user_id = ub.user_id
58
+ AND ub2.badge_type = 'recruiter'
59
+ )
60
+ ON CONFLICT (user_id, badge_type) DO NOTHING;
61
+
62
+ -- Award ambassador badge if user has captain
63
+ INSERT INTO user_badges (user_id, badge_type, badge_name, badge_description, badge_icon, referral_count)
64
+ SELECT DISTINCT
65
+ ub.user_id,
66
+ 'ambassador',
67
+ 'Ambassador',
68
+ 'Referred 5 users',
69
+ '/badges/badge_1-removebg-preview.png',
70
+ ub.referral_count
71
+ FROM user_badges ub
72
+ WHERE ub.badge_type = 'captain'
73
+ AND NOT EXISTS (
74
+ SELECT 1 FROM user_badges ub2
75
+ WHERE ub2.user_id = ub.user_id
76
+ AND ub2.badge_type = 'ambassador'
77
+ )
78
+ ON CONFLICT (user_id, badge_type) DO NOTHING;
79
+
80
+ -- Show results
81
+ SELECT
82
+ u.username,
83
+ u.wallet_address,
84
+ COUNT(referred.id) as referrals,
85
+ STRING_AGG(ub.badge_name, ', ') as badges
86
+ FROM users u
87
+ LEFT JOIN users referred ON referred.referral_code = u.my_referral_code
88
+ LEFT JOIN user_badges ub ON ub.user_id = u.id
89
+ WHERE u.my_referral_code IS NOT NULL
90
+ GROUP BY u.id, u.username, u.wallet_address
91
+ ORDER BY referrals DESC;
@@ -0,0 +1,41 @@
1
+ -- Fix payment notification constraint on Heroku
2
+ -- Run this if payments aren't working after deployment
3
+
4
+ -- Drop the old constraint
5
+ ALTER TABLE chat_notifications
6
+ DROP CONSTRAINT IF EXISTS chat_notifications_notification_type_check;
7
+
8
+ -- Add new constraint with payment types
9
+ ALTER TABLE chat_notifications
10
+ ADD CONSTRAINT chat_notifications_notification_type_check
11
+ CHECK (notification_type IN (
12
+ 'reply',
13
+ 'mention',
14
+ 'friend_message',
15
+ 'reaction',
16
+ 'friend_request',
17
+ 'friend_request_accepted',
18
+ 'friend_request_declined',
19
+ 'referral',
20
+ 'game_joined',
21
+ 'game_invite',
22
+ 'game_starting_soon',
23
+ 'game_starting_now',
24
+ 'payment_received',
25
+ 'payment_sent'
26
+ ));
27
+
28
+ -- Verify it worked
29
+ SELECT constraint_name, check_clause
30
+ FROM information_schema.check_constraints
31
+ WHERE constraint_name = 'chat_notifications_notification_type_check';
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ // NUCLEAR OPTION: Force increment config round ID to skip stuck round
3
+
4
+ const { Connection, Keypair, PublicKey, Transaction, TransactionInstruction } = require('@solana/web3.js');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const RPC_URL = 'https://api.devnet.solana.com';
9
+ const PROGRAM_ID = new PublicKey('BHidyz25KWkNPdTHgeANzMg25MM2KEiNnG4yE5F46XUz');
10
+
11
+ async function main() {
12
+ const oracleKeyPath = path.join(__dirname, '..', 'wallets', 'jackpot_oracle.json');
13
+ const secretKey = JSON.parse(fs.readFileSync(oracleKeyPath, 'utf-8'));
14
+ const wallet = Keypair.fromSecretKey(Uint8Array.from(secretKey));
15
+
16
+ console.log('💣 NUCLEAR OPTION: Opening round 98 to skip 97');
17
+
18
+ const connection = new Connection(RPC_URL, 'confirmed');
19
+
20
+ const [configPda] = PublicKey.findProgramAddressSync([Buffer.from('config')], PROGRAM_ID);
21
+ const [round98Pda] = PublicKey.findProgramAddressSync([Buffer.from('round'), Buffer.from([98,0,0,0,0,0,0,0])], PROGRAM_ID);
22
+ const [entries98Pda] = PublicKey.findProgramAddressSync([Buffer.from('entries'), Buffer.from([98,0,0,0,0,0,0,0])], PROGRAM_ID);
23
+
24
+ const OPEN_ROUND = Buffer.from([66, 235, 123, 240, 8, 35, 185, 159]);
25
+
26
+ const ix = new TransactionInstruction({
27
+ keys: [
28
+ { pubkey: configPda, isSigner: false, isWritable: true },
29
+ { pubkey: round98Pda, isSigner: false, isWritable: true },
30
+ { pubkey: entries98Pda, isSigner: false, isWritable: true },
31
+ { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
32
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
33
+ ],
34
+ programId: PROGRAM_ID,
35
+ data: OPEN_ROUND,
36
+ });
37
+
38
+ const tx = new Transaction().add(ix);
39
+ tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
40
+ tx.feePayer = wallet.publicKey;
41
+ tx.sign(wallet);
42
+
43
+ try {
44
+ const sig = await connection.sendRawTransaction(tx.serialize());
45
+ console.log('Sig:', sig);
46
+ await connection.confirmTransaction(sig);
47
+ console.log('✅ Round 98 opened! System should be unstuck now!');
48
+ } catch (e) {
49
+ console.error('❌', e.message);
50
+ }
51
+ }
52
+
53
+ const { SystemProgram } = require('@solana/web3.js');
54
+ main();
55
+
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Force Resolve and Claim Script
4
+ * Resolves stuck games and claims refunds directly using on-chain data
5
+ *
6
+ * Usage:
7
+ * node force-resolve-and-claim.js
8
+ */
9
+
10
+ require('dotenv').config();
11
+ const {
12
+ Connection,
13
+ PublicKey,
14
+ Keypair,
15
+ Transaction,
16
+ TransactionInstruction,
17
+ sendAndConfirmTransaction,
18
+ LAMPORTS_PER_SOL
19
+ } = require('@solana/web3.js');
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ // ═══════════════════════════════════════════════════════════════════════════
24
+ // CONFIGURATION - Update these values!
25
+ // ═══════════════════════════════════════════════════════════════════════════
26
+
27
+ // The stuck games to recover (from on-chain analysis)
28
+ const STUCK_GAMES = [
29
+ {
30
+ pda: 'FrggqLKgxdiTLybXK3fU8qpFG7YPtyh6fP2Y5onYWGhq',
31
+ gameId: BigInt('16210635126547177265'),
32
+ player: 'FWUJCthDfPcgmTvdQWM5uofxxiYjqJFMMwiLYvS7LBFa',
33
+ },
34
+ {
35
+ pda: 'DQGXTTewkW6QSSWTgaGTS8naepD6yDBumRRezhnpRkLK',
36
+ gameId: BigInt('13313520635794528485'),
37
+ player: 'FWUJCthDfPcgmTvdQWM5uofxxiYjqJFMMwiLYvS7LBFa',
38
+ },
39
+ {
40
+ pda: '6Bhvh1f9H1X3VMigVXLQNKoxCUftcTcnMCPGqzX4aBY',
41
+ gameId: BigInt('4788170354430293005'),
42
+ player: 'FWUJCthDfPcgmTvdQWM5uofxxiYjqJFMMwiLYvS7LBFa',
43
+ },
44
+ ];
45
+
46
+ // Program ID (mainnet)
47
+ const PROGRAM_ID = new PublicKey('85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
48
+
49
+ // Hardcoded operator wallet (from program)
50
+ const OPERATOR_WALLET = new PublicKey('BVZXwZpfgyzTBdRFHohkHZppPHnAyqyctRsKy3vWfQib');
51
+
52
+ // Instruction discriminators (calculated from Anchor)
53
+ const RESOLVE_AUTO_DISCRIMINATOR = Buffer.from([245, 33, 115, 150, 82, 150, 28, 193]);
54
+ const CLAIM_AUTO_DISCRIMINATOR = Buffer.from([8, 125, 228, 42, 245, 90, 82, 17]);
55
+
56
+ // RPC endpoint - use Alchemy for reliability
57
+ const RPC_URL = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com';
58
+
59
+ // ═══════════════════════════════════════════════════════════════════════════
60
+
61
+ console.log('🔧 Force Resolve and Claim Tool');
62
+ console.log('================================');
63
+ console.log(`RPC: ${RPC_URL}`);
64
+ console.log(`Program: ${PROGRAM_ID.toString()}`);
65
+ console.log('');
66
+
67
+ const connection = new Connection(RPC_URL, 'confirmed');
68
+
69
+ /**
70
+ * Load oracle wallet from environment or file
71
+ */
72
+ function loadOracleWallet() {
73
+ // Try environment variable first
74
+ if (process.env.ORACLE_WALLET_JSON) {
75
+ console.log('🔑 Loading oracle wallet from ORACLE_WALLET_JSON env var');
76
+ const secretKey = JSON.parse(process.env.ORACLE_WALLET_JSON);
77
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
78
+ }
79
+
80
+ // Try file path
81
+ const walletPath = process.env.ORACLE_WALLET_PATH || path.join(__dirname, 'wallets/oracle.json');
82
+
83
+ if (fs.existsSync(walletPath)) {
84
+ console.log('🔑 Loading oracle wallet from:', walletPath);
85
+ const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf-8'));
86
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
87
+ }
88
+
89
+ console.error('❌ Oracle wallet not found!');
90
+ console.log(' Set ORACLE_WALLET_JSON env var or create wallets/oracle.json');
91
+ process.exit(1);
92
+ }
93
+
94
+ /**
95
+ * Build resolve_automatic_game instruction
96
+ * Sets winning_team to None (0 option tag) for refund
97
+ */
98
+ function buildResolveInstruction(gamePda, gameId, oracleWallet) {
99
+ // Instruction data: discriminator (8) + game_id (8) + winning_team Option (1 byte = None)
100
+ const gameIdBuf = Buffer.alloc(8);
101
+ gameIdBuf.writeBigUInt64LE(gameId);
102
+
103
+ // Option<TeamChoice>: 0 = None (refund all)
104
+ const winningTeamBuf = Buffer.from([0]); // None variant
105
+
106
+ const data = Buffer.concat([
107
+ RESOLVE_AUTO_DISCRIMINATOR,
108
+ gameIdBuf,
109
+ winningTeamBuf,
110
+ ]);
111
+
112
+ return new TransactionInstruction({
113
+ keys: [
114
+ { pubkey: new PublicKey(gamePda), isSigner: false, isWritable: true },
115
+ { pubkey: oracleWallet.publicKey, isSigner: true, isWritable: true },
116
+ { pubkey: OPERATOR_WALLET, isSigner: false, isWritable: true }, // Receives operator fee
117
+ ],
118
+ programId: PROGRAM_ID,
119
+ data,
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Build claim_automatic_winnings instruction
125
+ */
126
+ function buildClaimInstruction(gamePda, gameId, playerWallet) {
127
+ // Instruction data: discriminator (8) + game_id (8)
128
+ const gameIdBuf = Buffer.alloc(8);
129
+ gameIdBuf.writeBigUInt64LE(gameId);
130
+
131
+ const data = Buffer.concat([
132
+ CLAIM_AUTO_DISCRIMINATOR,
133
+ gameIdBuf,
134
+ ]);
135
+
136
+ return new TransactionInstruction({
137
+ keys: [
138
+ { pubkey: new PublicKey(gamePda), isSigner: false, isWritable: true },
139
+ { pubkey: new PublicKey(playerWallet), isSigner: true, isWritable: true },
140
+ ],
141
+ programId: PROGRAM_ID,
142
+ data,
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Poll for transaction confirmation (Agave 2.0 compatible)
148
+ */
149
+ async function confirmTransaction(signature, timeout = 60000) {
150
+ const start = Date.now();
151
+
152
+ while (Date.now() - start < timeout) {
153
+ const statuses = await connection.getSignatureStatuses([signature]);
154
+ const status = statuses?.value?.[0];
155
+
156
+ if (status?.err) {
157
+ throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
158
+ }
159
+
160
+ if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
161
+ return status;
162
+ }
163
+
164
+ await new Promise(resolve => setTimeout(resolve, 1000));
165
+ }
166
+
167
+ throw new Error('Transaction confirmation timeout');
168
+ }
169
+
170
+ /**
171
+ * Resolve a single game
172
+ */
173
+ async function resolveGame(game, oracleWallet) {
174
+ console.log(`\n📍 Resolving game: ${game.pda}`);
175
+ console.log(` Game ID: ${game.gameId.toString()}`);
176
+
177
+ try {
178
+ const ix = buildResolveInstruction(game.pda, game.gameId, oracleWallet);
179
+ const tx = new Transaction().add(ix);
180
+
181
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
182
+ tx.recentBlockhash = blockhash;
183
+ tx.feePayer = oracleWallet.publicKey;
184
+
185
+ // Sign FIRST before sending
186
+ tx.sign(oracleWallet);
187
+
188
+ console.log(' 📡 Sending resolve transaction...');
189
+ const signature = await connection.sendRawTransaction(tx.serialize(), {
190
+ skipPreflight: false,
191
+ preflightCommitment: 'confirmed',
192
+ });
193
+
194
+ console.log(` 📝 Signature: ${signature}`);
195
+ console.log(' ⏳ Confirming...');
196
+ await confirmTransaction(signature);
197
+
198
+ console.log(` ✅ Resolved!`);
199
+ return signature;
200
+
201
+ } catch (error) {
202
+ // Try to get logs for more details
203
+ if (error.logs) {
204
+ console.error(' 📋 Logs:', error.logs.join('\n '));
205
+ }
206
+ if (error.message?.includes('AlreadyResolved')) {
207
+ console.log(' ℹ️ Game already resolved');
208
+ return 'already_resolved';
209
+ }
210
+ if (error.message?.includes('CannotResolveBeforeLockTime')) {
211
+ console.log(' ⏰ Cannot resolve before lock time (game hasn\'t started yet)');
212
+ return 'too_early';
213
+ }
214
+ console.error(` ❌ Error: ${error.message}`);
215
+ throw error;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Main execution
221
+ */
222
+ async function main() {
223
+ const args = process.argv.slice(2);
224
+
225
+ console.log('📊 Games to recover:');
226
+ STUCK_GAMES.forEach((game, i) => {
227
+ console.log(` ${i + 1}. PDA: ${game.pda.slice(0, 12)}... | Player: ${game.player.slice(0, 12)}...`);
228
+ });
229
+
230
+ // Check if --resolve flag is passed
231
+ if (args.includes('--resolve')) {
232
+ console.log('\n🔐 Loading oracle wallet...');
233
+ const oracleWallet = loadOracleWallet();
234
+ console.log(` Oracle: ${oracleWallet.publicKey.toString()}`);
235
+
236
+ // Verify oracle matches expected
237
+ const expectedOracle = 'FWUJCthDfPcgmTvdQWM5uofxxiYjqJFMMwiLYvS7LBFa';
238
+ if (oracleWallet.publicKey.toString() !== expectedOracle) {
239
+ console.error(` ⚠️ Warning: Oracle doesn't match expected (${expectedOracle})`);
240
+ }
241
+
242
+ console.log('\n🔧 Resolving games...');
243
+
244
+ for (const game of STUCK_GAMES) {
245
+ try {
246
+ await resolveGame(game, oracleWallet);
247
+ } catch (error) {
248
+ console.error(` Failed to resolve ${game.pda}: ${error.message}`);
249
+ }
250
+ }
251
+
252
+ console.log('\n✅ Resolution complete!');
253
+ console.log('💡 Now use the frontend to claim your refunds, or run:');
254
+ console.log(' node force-resolve-and-claim.js --claim <playerWalletPath>');
255
+
256
+ } else if (args.includes('--claim')) {
257
+ // For claiming, the player needs to sign
258
+ console.log('\n💰 Claim mode');
259
+ console.log(' To claim, use the frontend "Claim" button in My Games');
260
+ console.log(' Or call the API: POST /api/v1/prod/transaction/build/claim-automatic');
261
+ console.log(' With body: { "playerAddress": "<wallet>", "gameId": "<gameId>" }');
262
+
263
+ } else {
264
+ console.log('\nUsage:');
265
+ console.log(' node force-resolve-and-claim.js --resolve # Resolve all stuck games (requires oracle wallet)');
266
+ console.log(' node force-resolve-and-claim.js --claim # Show claim instructions');
267
+ console.log('');
268
+ console.log('Prerequisites:');
269
+ console.log(' 1. Set ORACLE_WALLET_JSON env var with oracle private key array');
270
+ console.log(' OR create wallets/oracle.json with the keypair');
271
+ console.log(' 2. Oracle must match the one registered in the game accounts');
272
+ console.log('');
273
+ }
274
+ }
275
+
276
+ main().catch(console.error);
277
+
278
+
@@ -0,0 +1,115 @@
1
+ # Important Scripts
2
+
3
+ Scripts for managing games, payouts, and emergency operations.
4
+
5
+ ---
6
+
7
+ ## check-game-status.js
8
+
9
+ Shows complete game status including all players, their bets, win/loss results, claim status, and expected payouts.
10
+
11
+ **Usage:**
12
+ ```bash
13
+ DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/important/check-game-status.js <gameId>
14
+ ```
15
+
16
+ **Example:**
17
+ ```bash
18
+ DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/important/check-game-status.js sport-1769102132440-xmirk1omx
19
+ ```
20
+
21
+ **Output includes:**
22
+ - Game info (teams, score, winner, buy-in, resolved status)
23
+ - Pot breakdown (total pot, platform fee, winner pool)
24
+ - Player table with bets, results, claim status, and payouts
25
+ - Claim transaction signatures and explorer links
26
+ - Summary of pending claims
27
+
28
+ ---
29
+
30
+ ## reconcile-v1-v2-payouts.js
31
+
32
+ Calculates the difference between V1 (equal split) and V2 (proportional) payouts for any game. Shows what you would owe players to make V1 payouts fair under V2 rules.
33
+
34
+ **Usage:**
35
+ ```bash
36
+ DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/important/reconcile-v1-v2-payouts.js <gameId>
37
+ ```
38
+
39
+ **Output includes:**
40
+ - Pot details and team pools
41
+ - Scenarios for each possible winner (home/away/draw)
42
+ - Per-player breakdown: V1 payout vs V2 payout vs difference owed
43
+ - Total reconciliation amount needed
44
+ - If game is resolved: specific players and wallets to reimburse
45
+
46
+ ---
47
+
48
+ ## list-pending-games-by-version.js
49
+
50
+ Lists all pending (unresolved) games categorized by version:
51
+ - **V1 (Legacy/Hybrid):** Equal split payouts - `player_amounts.length != total_players`
52
+ - **V2 (Pari-Mutuel):** Proportional payouts - all players in `player_amounts`
53
+
54
+ **Usage:**
55
+ ```bash
56
+ DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/important/list-pending-games-by-version.js
57
+ ```
58
+
59
+ **Output includes:**
60
+ - Games grouped by V1 vs V2
61
+ - Per-game details: matchup, pot size, team pools, odds
62
+ - Player list with bets and expected payouts
63
+ - Summary totals
64
+
65
+ ---
66
+
67
+ ## authority-force-lock.js
68
+
69
+ Forces a game to locked state using program authority. This allows the oracle to resolve games that were created with future lock times (e.g., timezone issues, events ending early).
70
+
71
+ **Usage:**
72
+ ```bash
73
+ SOLANA_RPC_URL="https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY" node scripts/important/authority-force-lock.js <gameId>
74
+ ```
75
+
76
+ **When to use:**
77
+ - Oracle shows "CannotResolveBeforeLockTime" error
78
+ - Game event has ended but lock time is in the future
79
+ - Need to manually trigger resolution
80
+
81
+ **Requirements:**
82
+ - Program authority wallet at `~/.config/solana/id.json`
83
+ - Mainnet RPC URL
84
+
85
+ ---
86
+
87
+ ## authority-resolve-game.js
88
+
89
+ Emergency resolve or refund games using program authority. Can force resolve with a winner or refund all players.
90
+
91
+ **Usage:**
92
+ ```bash
93
+ SOLANA_RPC_URL="https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY" node scripts/important/authority-resolve-game.js <gameId> [winner]
94
+ ```
95
+
96
+ **Examples:**
97
+ ```bash
98
+ # Refund all players (no winner)
99
+ node scripts/important/authority-resolve-game.js sport-123
100
+
101
+ # Resolve with home team winning
102
+ node scripts/important/authority-resolve-game.js sport-123 home
103
+
104
+ # Resolve with away team winning
105
+ node scripts/important/authority-resolve-game.js sport-123 away
106
+ ```
107
+
108
+ **When to use:**
109
+ - Postponed or cancelled games that need refunding
110
+ - Stuck games where oracle failed
111
+ - Disputed games requiring manual resolution
112
+
113
+ **Requirements:**
114
+ - Program authority wallet at `~/.config/solana/id.json`
115
+ - Mainnet RPC URL