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,113 @@
1
+ #!/bin/bash
2
+
3
+ # ============================================
4
+ # šŸŽ® REBUILD HEROKU DATABASE FROM SCRATCH
5
+ # ============================================
6
+ # This script completely nukes and rebuilds the Heroku database
7
+ # with the complete new schema
8
+
9
+ set -e
10
+
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ BLUE='\033[0;34m'
15
+ NC='\033[0m'
16
+
17
+ # Get DATABASE_URL from Heroku
18
+ echo "šŸ“” Getting Heroku database URL..."
19
+ DB_URL=$(heroku config:get DATABASE_URL 2>&1 | grep -v "Warning" | grep -v "Error ID" | grep "postgres://")
20
+
21
+ if [ -z "$DB_URL" ]; then
22
+ echo -e "${RED}āŒ Could not get DATABASE_URL from Heroku${NC}"
23
+ echo "Make sure you're in the right directory with Heroku app configured"
24
+ exit 1
25
+ fi
26
+
27
+ echo -e "${GREEN}āœ… Found Heroku database${NC}"
28
+ echo ""
29
+
30
+ echo "╔════════════════════════════════════════════════╗"
31
+ echo "ā•‘ āš ļø NUKE HEROKU PRODUCTION DATABASE āš ļø ā•‘"
32
+ echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•"
33
+ echo ""
34
+ echo -e "${RED}This will DELETE EVERYTHING and rebuild from scratch!${NC}"
35
+ echo ""
36
+ echo "Database: $(echo $DB_URL | sed 's/postgres:\/\/.*@/postgres:\/\/*****@/')"
37
+ echo ""
38
+
39
+ read -p "Type 'NUKE' to confirm: " -r
40
+ echo ""
41
+
42
+ if [[ ! $REPLY == "NUKE" ]]; then
43
+ echo -e "${GREEN}āœ… Cancelled${NC}"
44
+ exit 0
45
+ fi
46
+
47
+ echo ""
48
+ echo "šŸ—‘ļø Step 1: Dropping all tables..."
49
+ echo ""
50
+
51
+ psql "$DB_URL" << 'DROPSQL'
52
+ -- Drop all views first
53
+ DROP VIEW IF EXISTS keeper_health_summary CASCADE;
54
+ DROP VIEW IF EXISTS stuck_rounds CASCADE;
55
+
56
+ -- Drop all tables
57
+ DROP TABLE IF EXISTS chat_notifications CASCADE;
58
+ DROP TABLE IF EXISTS chat_reactions CASCADE;
59
+ DROP TABLE IF EXISTS chat_messages CASCADE;
60
+ DROP TABLE IF EXISTS user_relationships CASCADE;
61
+ DROP TABLE IF EXISTS friend_requests CASCADE;
62
+ DROP TABLE IF EXISTS friends CASCADE;
63
+ DROP TABLE IF EXISTS group_members CASCADE;
64
+ DROP TABLE IF EXISTS groups CASCADE;
65
+ DROP TABLE IF EXISTS player_history CASCADE;
66
+ DROP TABLE IF EXISTS player_stats CASCADE;
67
+ DROP TABLE IF EXISTS user_badges CASCADE;
68
+ DROP TABLE IF EXISTS auth_nonces CASCADE;
69
+ DROP TABLE IF EXISTS user_sessions CASCADE;
70
+ DROP TABLE IF EXISTS keeper_actions CASCADE;
71
+ DROP TABLE IF EXISTS keeper_health CASCADE;
72
+ DROP TABLE IF EXISTS keeper_rounds CASCADE;
73
+ DROP TABLE IF EXISTS jackpot_rounds CASCADE;
74
+ DROP TABLE IF EXISTS users CASCADE;
75
+
76
+ SELECT 'All tables dropped' as status;
77
+ DROPSQL
78
+
79
+ echo -e "${GREEN}āœ… All tables dropped${NC}"
80
+ echo ""
81
+ echo "šŸ—ļø Step 2: Creating complete schema..."
82
+ echo ""
83
+
84
+ # Run the complete schema setup
85
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
86
+ psql "$DB_URL" < "$SCRIPT_DIR/setup-complete-database.sql"
87
+
88
+ echo ""
89
+ echo -e "${GREEN}āœ… Schema created${NC}"
90
+ echo ""
91
+ echo "šŸ“Š Step 3: Verifying setup..."
92
+ echo ""
93
+
94
+ # Count tables
95
+ TABLE_COUNT=$(psql "$DB_URL" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE';" | xargs)
96
+
97
+ echo "Tables created: $TABLE_COUNT"
98
+ echo ""
99
+
100
+ # List tables
101
+ psql "$DB_URL" -c "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name;"
102
+
103
+ echo ""
104
+ echo "╔════════════════════════════════════════════════╗"
105
+ echo "ā•‘ āœ… HEROKU DATABASE REBUILT! āœ… ā•‘"
106
+ echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•"
107
+ echo ""
108
+ echo -e "${GREEN}āœ… Complete schema with $TABLE_COUNT tables${NC}"
109
+ echo -e "${GREEN}āœ… All indexes and views created${NC}"
110
+ echo -e "${GREEN}āœ… Ready for production use${NC}"
111
+ echo ""
112
+ echo "šŸš€ Your Heroku database is now fresh and ready!"
113
+ echo ""
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Fund Recovery Script
4
+ * Check game PDAs and help recover stuck funds
5
+ *
6
+ * Usage:
7
+ * node recover-funds.js <pda1> <pda2> ...
8
+ * node recover-funds.js --claim <gameId> <playerAddress>
9
+ */
10
+
11
+ require('dotenv').config();
12
+ const { Connection, PublicKey, Keypair, Transaction, TransactionInstruction, LAMPORTS_PER_SOL } = require('@solana/web3.js');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ // Program ID (mainnet)
17
+ const PROGRAM_ID = new PublicKey('85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
18
+
19
+ // Instruction discriminators
20
+ const CLAIM_AUTO = Buffer.from([8, 125, 228, 42, 245, 90, 82, 17]); // claim_automatic_winnings
21
+ const EMERGENCY_REFUND = Buffer.from([/* need to calculate */]); // emergency_refund_automatic
22
+
23
+ // RPC endpoint
24
+ const RPC_URL = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com';
25
+
26
+ console.log('šŸ’° Fund Recovery Tool');
27
+ console.log('=====================');
28
+ console.log(`RPC: ${RPC_URL}`);
29
+ console.log(`Program: ${PROGRAM_ID.toString()}`);
30
+ console.log('');
31
+
32
+ const connection = new Connection(RPC_URL, 'confirmed');
33
+
34
+ /**
35
+ * Decode game account data
36
+ */
37
+ function decodeGameAccount(data) {
38
+ try {
39
+ // Skip 8 byte discriminator
40
+ let offset = 8;
41
+
42
+ // game_id: u64
43
+ const gameId = data.readBigUInt64LE(offset);
44
+ offset += 8;
45
+
46
+ // authority: Pubkey (32 bytes)
47
+ const authority = new PublicKey(data.slice(offset, offset + 32));
48
+ offset += 32;
49
+
50
+ // buy_in: u64
51
+ const buyIn = data.readBigUInt64LE(offset);
52
+ offset += 8;
53
+
54
+ // max_players: u8
55
+ const maxPlayers = data.readUInt8(offset);
56
+ offset += 1;
57
+
58
+ // operator_wallet: Pubkey (32 bytes)
59
+ const operatorWallet = new PublicKey(data.slice(offset, offset + 32));
60
+ offset += 32;
61
+
62
+ // operator_fee_percentage: u8
63
+ const operatorFee = data.readUInt8(offset);
64
+ offset += 1;
65
+
66
+ // players vec: 4 bytes length + 32 bytes * length
67
+ const playersLen = data.readUInt32LE(offset);
68
+ offset += 4;
69
+ const players = [];
70
+ for (let i = 0; i < playersLen; i++) {
71
+ players.push(new PublicKey(data.slice(offset, offset + 32)));
72
+ offset += 32;
73
+ }
74
+
75
+ // total_pot: u64
76
+ const totalPot = data.readBigUInt64LE(offset);
77
+ offset += 8;
78
+
79
+ // is_active: bool
80
+ const isActive = data.readUInt8(offset) === 1;
81
+ offset += 1;
82
+
83
+ // votes vec: skip for now (4 bytes length + entries)
84
+ const votesLen = data.readUInt32LE(offset);
85
+ offset += 4;
86
+ offset += votesLen * (32 + 32); // voter + voted_for
87
+
88
+ // voting_enabled: bool
89
+ const votingEnabled = data.readUInt8(offset) === 1;
90
+ offset += 1;
91
+
92
+ // game_mode: u8
93
+ const gameMode = data.readUInt8(offset);
94
+ offset += 1;
95
+
96
+ // referee: Option<Pubkey> - 1 byte tag + 32 bytes if Some
97
+ const hasReferee = data.readUInt8(offset) === 1;
98
+ offset += 1;
99
+ if (hasReferee) {
100
+ offset += 32;
101
+ }
102
+
103
+ // referee_commission: u8
104
+ const refereeCommission = data.readUInt8(offset);
105
+ offset += 1;
106
+
107
+ // player_picks vec: 4 bytes length + (32 + 1) bytes * length
108
+ const picksLen = data.readUInt32LE(offset);
109
+ offset += 4;
110
+ const playerPicks = [];
111
+ for (let i = 0; i < picksLen; i++) {
112
+ const player = new PublicKey(data.slice(offset, offset + 32));
113
+ offset += 32;
114
+ const teamChoice = data.readUInt8(offset); // 0 = Home, 1 = Away
115
+ offset += 1;
116
+ playerPicks.push({ player: player.toString(), teamChoice: teamChoice === 0 ? 'Home' : 'Away' });
117
+ }
118
+
119
+ // lock_timestamp: i64
120
+ const lockTimestamp = Number(data.readBigInt64LE(offset));
121
+ offset += 8;
122
+
123
+ // is_locked: bool
124
+ const isLocked = data.readUInt8(offset) === 1;
125
+ offset += 1;
126
+
127
+ // is_resolved: bool
128
+ const isResolved = data.readUInt8(offset) === 1;
129
+ offset += 1;
130
+
131
+ // winning_team: Option<TeamChoice>
132
+ const hasWinner = data.readUInt8(offset) === 1;
133
+ offset += 1;
134
+ let winningTeam = null;
135
+ if (hasWinner) {
136
+ winningTeam = data.readUInt8(offset) === 0 ? 'Home' : 'Away';
137
+ offset += 1;
138
+ }
139
+
140
+ // sports_event_id: String (4 bytes length + chars)
141
+ const eventIdLen = data.readUInt32LE(offset);
142
+ offset += 4;
143
+ const sportsEventId = data.slice(offset, offset + eventIdLen).toString('utf8');
144
+ offset += eventIdLen;
145
+
146
+ // oracle: Pubkey
147
+ const oracle = new PublicKey(data.slice(offset, offset + 32));
148
+ offset += 32;
149
+
150
+ // claimed_players vec
151
+ const claimedLen = data.readUInt32LE(offset);
152
+ offset += 4;
153
+ const claimedPlayers = [];
154
+ for (let i = 0; i < claimedLen; i++) {
155
+ claimedPlayers.push(new PublicKey(data.slice(offset, offset + 32)).toString());
156
+ offset += 32;
157
+ }
158
+
159
+ // operator_fee_claimed: bool
160
+ const operatorFeeClaimed = data.readUInt8(offset) === 1;
161
+
162
+ return {
163
+ gameId: gameId.toString(),
164
+ authority: authority.toString(),
165
+ buyIn: Number(buyIn) / LAMPORTS_PER_SOL,
166
+ maxPlayers,
167
+ operatorWallet: operatorWallet.toString(),
168
+ operatorFee,
169
+ players: players.map(p => p.toString()),
170
+ totalPot: Number(totalPot) / LAMPORTS_PER_SOL,
171
+ isActive,
172
+ votingEnabled,
173
+ gameMode: ['Manual', 'Democratic', 'Referee', 'Automatic'][gameMode],
174
+ playerPicks,
175
+ lockTimestamp: new Date(lockTimestamp * 1000).toISOString(),
176
+ isLocked,
177
+ isResolved,
178
+ winningTeam,
179
+ sportsEventId,
180
+ oracle: oracle.toString(),
181
+ claimedPlayers,
182
+ operatorFeeClaimed,
183
+ };
184
+ } catch (error) {
185
+ console.error('Decode error:', error.message);
186
+ return null;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Check a single game PDA
192
+ */
193
+ async function checkGamePDA(pdaAddress) {
194
+ console.log(`\nšŸ“ Checking PDA: ${pdaAddress}`);
195
+ console.log('─'.repeat(60));
196
+
197
+ try {
198
+ const pda = new PublicKey(pdaAddress);
199
+ const accountInfo = await connection.getAccountInfo(pda);
200
+
201
+ if (!accountInfo) {
202
+ console.log(' āŒ Account does not exist (funds may have been withdrawn)');
203
+ return null;
204
+ }
205
+
206
+ console.log(` šŸ’° Balance: ${accountInfo.lamports / LAMPORTS_PER_SOL} SOL`);
207
+ console.log(` šŸ“¦ Data size: ${accountInfo.data.length} bytes`);
208
+ console.log(` šŸ‘¤ Owner: ${accountInfo.owner.toString()}`);
209
+
210
+ if (!accountInfo.owner.equals(PROGRAM_ID)) {
211
+ console.log(' āš ļø Not owned by Dubs program!');
212
+ return null;
213
+ }
214
+
215
+ const game = decodeGameAccount(accountInfo.data);
216
+
217
+ if (!game) {
218
+ console.log(' āš ļø Could not decode game data');
219
+ return null;
220
+ }
221
+
222
+ console.log(`\n šŸ“Š Game State:`);
223
+ console.log(` Game ID: ${game.gameId}`);
224
+ console.log(` Mode: ${game.gameMode}`);
225
+ console.log(` Buy-in: ${game.buyIn} SOL`);
226
+ console.log(` Prize Pool: ${game.totalPot} SOL`);
227
+ console.log(` Active: ${game.isActive}`);
228
+ console.log(` Locked: ${game.isLocked}`);
229
+ console.log(` Resolved: ${game.isResolved}`);
230
+ console.log(` Winner: ${game.winningTeam || 'None (refund all)'}`);
231
+ console.log(` Lock Time: ${game.lockTimestamp}`);
232
+ console.log(` Sports Event: ${game.sportsEventId || 'N/A'}`);
233
+
234
+ console.log(`\n šŸ‘„ Player Picks (${game.playerPicks.length}):`);
235
+ game.playerPicks.forEach((pick, i) => {
236
+ const claimed = game.claimedPlayers.includes(pick.player) ? 'āœ… CLAIMED' : 'ā³ Unclaimed';
237
+ console.log(` ${i + 1}. ${pick.player.slice(0, 8)}... → ${pick.teamChoice} ${claimed}`);
238
+ });
239
+
240
+ if (game.claimedPlayers.length > 0) {
241
+ console.log(`\n āœ… Claimed Players (${game.claimedPlayers.length}):`);
242
+ game.claimedPlayers.forEach((p, i) => {
243
+ console.log(` ${i + 1}. ${p.slice(0, 8)}...`);
244
+ });
245
+ }
246
+
247
+ // Determine recovery action
248
+ console.log(`\n šŸ”§ Recovery Status:`);
249
+
250
+ if (game.isResolved) {
251
+ if (game.winningTeam === null) {
252
+ console.log(` āœ… Game resolved as TIE/REFUND - all players can claim!`);
253
+ } else {
254
+ console.log(` āœ… Game resolved - ${game.winningTeam} team won`);
255
+ }
256
+
257
+ const unclaimedPlayers = game.playerPicks
258
+ .filter(p => !game.claimedPlayers.includes(p.player))
259
+ .map(p => p.player);
260
+
261
+ if (unclaimedPlayers.length > 0) {
262
+ console.log(` šŸ“¢ ${unclaimedPlayers.length} player(s) can still claim!`);
263
+ console.log(` šŸ’” Use: node recover-funds.js --claim ${game.gameId} <yourWalletAddress>`);
264
+ } else {
265
+ console.log(` āœ… All players have claimed`);
266
+ }
267
+ } else {
268
+ const now = Date.now();
269
+ const lockTime = new Date(game.lockTimestamp).getTime();
270
+ const sevenDays = 7 * 24 * 60 * 60 * 1000;
271
+
272
+ if (now > lockTime + sevenDays) {
273
+ console.log(` āš ļø NOT resolved but 7 days have passed!`);
274
+ console.log(` šŸ’” Emergency refund is available!`);
275
+ console.log(` šŸ’” Oracle or anyone can trigger emergency_refund_automatic`);
276
+ } else {
277
+ const daysUntil = Math.ceil((lockTime + sevenDays - now) / (24 * 60 * 60 * 1000));
278
+ console.log(` ā³ NOT resolved yet`);
279
+ console.log(` ā³ Emergency refund available in ${daysUntil} days`);
280
+ console.log(` šŸ’” Oracle needs to resolve this game first`);
281
+ console.log(` šŸ’” Or wait for emergency refund window`);
282
+ }
283
+ }
284
+
285
+ return game;
286
+
287
+ } catch (error) {
288
+ console.log(` āŒ Error: ${error.message}`);
289
+ return null;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Build claim transaction
295
+ */
296
+ async function buildClaimTransaction(gameId, playerAddress) {
297
+ console.log(`\nšŸ”Ø Building claim transaction...`);
298
+ console.log(` Game ID: ${gameId}`);
299
+ console.log(` Player: ${playerAddress}`);
300
+
301
+ try {
302
+ // Convert gameId to buffer
303
+ // The gameId in the account is a u64, but we store it as a hash of the string gameId
304
+ // We need to figure out the PDA derivation
305
+
306
+ // For now, output instructions for manual claim via API
307
+ console.log(`\n šŸ’” To claim, use the API endpoint:`);
308
+ console.log(` POST /api/v1/prod/transaction/build/claim-automatic`);
309
+ console.log(` Body: { "playerAddress": "${playerAddress}", "gameId": "${gameId}" }`);
310
+ console.log(`\n Or use the frontend claim button in My Games`);
311
+
312
+ } catch (error) {
313
+ console.error(` āŒ Error: ${error.message}`);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Main execution
319
+ */
320
+ async function main() {
321
+ const args = process.argv.slice(2);
322
+
323
+ if (args.length === 0) {
324
+ console.log('Usage:');
325
+ console.log(' node recover-funds.js <pda1> [pda2] [pda3] ... # Check game PDAs');
326
+ console.log(' node recover-funds.js --claim <gameId> <wallet> # Build claim tx');
327
+ console.log('');
328
+ console.log('Examples:');
329
+ console.log(' node recover-funds.js FrggqLKgxdiTLybXK3fU8qpFG7YPtyh6fP2Y5onYWGhq');
330
+ console.log(' node recover-funds.js --claim sport-123456-abc 7D47yF4qKdQGkT9xNWon...');
331
+ process.exit(0);
332
+ }
333
+
334
+ if (args[0] === '--claim') {
335
+ if (args.length < 3) {
336
+ console.error('āŒ Usage: node recover-funds.js --claim <gameId> <playerAddress>');
337
+ process.exit(1);
338
+ }
339
+ await buildClaimTransaction(args[1], args[2]);
340
+ } else {
341
+ // Check each PDA
342
+ const pdas = args.filter(a => !a.startsWith('--'));
343
+
344
+ console.log(`\nšŸ” Checking ${pdas.length} game PDA(s)...\n`);
345
+
346
+ for (const pda of pdas) {
347
+ await checkGamePDA(pda);
348
+ }
349
+
350
+ console.log('\n' + '═'.repeat(60));
351
+ console.log('✨ Done!\n');
352
+ }
353
+ }
354
+
355
+ main().catch(console.error);
356
+
357
+