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,382 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Manual Game Resolution Script
4
+ * Resolves stuck sports games when the oracle fails
5
+ *
6
+ * Usage:
7
+ * node resolve-stuck-game.js --game-id <game_id> --winner <home|away|tie>
8
+ *
9
+ * Example:
10
+ * node resolve-stuck-game.js --game-id sport-1766418200917-00ram5nn3 --winner home
11
+ */
12
+
13
+ require('dotenv').config();
14
+ const {
15
+ Connection,
16
+ PublicKey,
17
+ Keypair,
18
+ Transaction,
19
+ TransactionInstruction,
20
+ } = require('@solana/web3.js');
21
+ const crypto = require('crypto');
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+
25
+ // ═══════════════════════════════════════════════════════════════════════════
26
+ // CONFIGURATION
27
+ // ═══════════════════════════════════════════════════════════════════════════
28
+
29
+ // Program ID (mainnet - use env var or default)
30
+ const PROGRAM_ID = new PublicKey(process.env.PROGRAM_ID || '85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
31
+
32
+ // Hardcoded operator wallet (from program)
33
+ const OPERATOR_WALLET = new PublicKey('BVZXwZpfgyzTBdRFHohkHZppPHnAyqyctRsKy3vWfQib');
34
+
35
+ // Instruction discriminator for resolve_automatic_game
36
+ const RESOLVE_AUTO_DISCRIMINATOR = Buffer.from([245, 33, 115, 150, 82, 150, 28, 193]);
37
+
38
+ // RPC endpoint
39
+ const RPC_URL = process.env.SOLANA_RPC_URL || process.env.SOLANA_NETWORK || 'https://api.mainnet-beta.solana.com';
40
+
41
+ // ═══════════════════════════════════════════════════════════════════════════
42
+
43
+ console.log('🔧 Manual Game Resolution Tool');
44
+ console.log('================================');
45
+ console.log(`RPC: ${RPC_URL}`);
46
+ console.log(`Program: ${PROGRAM_ID.toString()}`);
47
+ console.log('');
48
+
49
+ const connection = new Connection(RPC_URL, 'confirmed');
50
+
51
+ /**
52
+ * Load oracle wallet from environment or file
53
+ */
54
+ function loadOracleWallet() {
55
+ // Try environment variable first (ORACLE_WALLET_JSON for Heroku)
56
+ if (process.env.ORACLE_WALLET_JSON) {
57
+ console.log('🔑 Loading oracle wallet from ORACLE_WALLET_JSON env var');
58
+ const secretKey = JSON.parse(process.env.ORACLE_WALLET_JSON);
59
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
60
+ }
61
+
62
+ // Try file path
63
+ const walletPaths = [
64
+ process.env.ORACLE_WALLET_PATH,
65
+ path.join(__dirname, '../wallets/oracle.json'),
66
+ path.join(__dirname, 'wallets/oracle.json'),
67
+ ].filter(Boolean);
68
+
69
+ for (const walletPath of walletPaths) {
70
+ if (fs.existsSync(walletPath)) {
71
+ console.log('🔑 Loading oracle wallet from:', walletPath);
72
+ const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf-8'));
73
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
74
+ }
75
+ }
76
+
77
+ console.error('❌ Oracle wallet not found!');
78
+ console.log(' Set ORACLE_WALLET_JSON env var or create wallets/oracle.json');
79
+ process.exit(1);
80
+ }
81
+
82
+ /**
83
+ * Convert string game ID to u64 using SHA256 hash (same as oracle)
84
+ */
85
+ function gameIdToU64(gameId) {
86
+ if (typeof gameId === 'string' && gameId.includes('-')) {
87
+ const hash = crypto.createHash('sha256').update(gameId).digest();
88
+ return hash.readBigUInt64LE(0);
89
+ }
90
+ return BigInt(gameId);
91
+ }
92
+
93
+ /**
94
+ * Get game PDA from game ID
95
+ */
96
+ function getGamePDA(gameId) {
97
+ const gameIdNum = gameIdToU64(gameId);
98
+ const gameIdBuf = Buffer.alloc(8);
99
+ gameIdBuf.writeBigUInt64LE(gameIdNum);
100
+
101
+ const [gamePDA] = PublicKey.findProgramAddressSync(
102
+ [Buffer.from("game"), gameIdBuf],
103
+ PROGRAM_ID
104
+ );
105
+
106
+ return { gamePDA, gameIdNum, gameIdBuf };
107
+ }
108
+
109
+ /**
110
+ * Build resolve_automatic_game instruction
111
+ */
112
+ function buildResolveInstruction(gamePDA, gameIdBuf, winner, oracleWallet) {
113
+ // Encode winning team: Some(Home) = [1, 0], Some(Away) = [1, 1], None (tie/refund) = [0]
114
+ let winningTeamBytes;
115
+ if (winner === 'tie' || winner === 'refund') {
116
+ winningTeamBytes = Buffer.from([0]); // None - refund all
117
+ } else if (winner === 'home') {
118
+ winningTeamBytes = Buffer.from([1, 0]); // Some(Home)
119
+ } else if (winner === 'away') {
120
+ winningTeamBytes = Buffer.from([1, 1]); // Some(Away)
121
+ } else {
122
+ throw new Error(`Invalid winner: ${winner}. Must be 'home', 'away', or 'tie'`);
123
+ }
124
+
125
+ const data = Buffer.concat([
126
+ RESOLVE_AUTO_DISCRIMINATOR,
127
+ gameIdBuf,
128
+ winningTeamBytes,
129
+ ]);
130
+
131
+ return new TransactionInstruction({
132
+ keys: [
133
+ { pubkey: gamePDA, isSigner: false, isWritable: true },
134
+ { pubkey: oracleWallet.publicKey, isSigner: true, isWritable: true }, // Receives 2% oracle fee
135
+ { pubkey: OPERATOR_WALLET, isSigner: false, isWritable: true }, // Receives 10% operator fee
136
+ ],
137
+ programId: PROGRAM_ID,
138
+ data,
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Poll for transaction confirmation
144
+ */
145
+ async function confirmTransaction(signature, timeout = 60000) {
146
+ const start = Date.now();
147
+
148
+ while (Date.now() - start < timeout) {
149
+ const statuses = await connection.getSignatureStatuses([signature]);
150
+ const status = statuses?.value?.[0];
151
+
152
+ if (status?.err) {
153
+ throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
154
+ }
155
+
156
+ if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
157
+ return status;
158
+ }
159
+
160
+ await new Promise(resolve => setTimeout(resolve, 1000));
161
+ }
162
+
163
+ throw new Error('Transaction confirmation timeout');
164
+ }
165
+
166
+ /**
167
+ * Resolve a game
168
+ */
169
+ async function resolveGame(gameId, winner, oracleWallet) {
170
+ console.log(`\n📍 Resolving game: ${gameId}`);
171
+ console.log(` Winner: ${winner.toUpperCase()}`);
172
+
173
+ const { gamePDA, gameIdNum, gameIdBuf } = getGamePDA(gameId);
174
+ console.log(` Game PDA: ${gamePDA.toString()}`);
175
+ console.log(` Game ID (u64): ${gameIdNum.toString()}`);
176
+
177
+ // Check if game account exists
178
+ const accountInfo = await connection.getAccountInfo(gamePDA);
179
+ if (!accountInfo) {
180
+ console.error(' ❌ Game account not found on-chain!');
181
+ console.log(' This could mean:');
182
+ console.log(' 1. The game was never created on-chain (only in database)');
183
+ console.log(' 2. The game ID is incorrect');
184
+ console.log(' 3. The program ID is wrong');
185
+ return null;
186
+ }
187
+ console.log(` ✅ Game account found (${accountInfo.data.length} bytes)`);
188
+
189
+ try {
190
+ const ix = buildResolveInstruction(gamePDA, gameIdBuf, winner, oracleWallet);
191
+ const tx = new Transaction().add(ix);
192
+
193
+ const { blockhash } = await connection.getLatestBlockhash();
194
+ tx.recentBlockhash = blockhash;
195
+ tx.feePayer = oracleWallet.publicKey;
196
+
197
+ // Sign the transaction
198
+ tx.sign(oracleWallet);
199
+
200
+ console.log(' 📡 Sending resolve transaction...');
201
+ const signature = await connection.sendRawTransaction(tx.serialize(), {
202
+ skipPreflight: false,
203
+ preflightCommitment: 'confirmed',
204
+ });
205
+
206
+ console.log(` 📝 Signature: ${signature}`);
207
+ console.log(` 🔗 https://solscan.io/tx/${signature}`);
208
+ console.log(' ⏳ Confirming...');
209
+
210
+ await confirmTransaction(signature);
211
+
212
+ console.log(` ✅ Game resolved successfully!`);
213
+ return signature;
214
+
215
+ } catch (error) {
216
+ if (error.logs) {
217
+ console.error(' 📋 Program Logs:');
218
+ error.logs.forEach(log => console.error(` ${log}`));
219
+ }
220
+
221
+ if (error.message?.includes('AlreadyResolved') || error.message?.includes('0x1796')) {
222
+ console.log(' ℹ️ Game is already resolved');
223
+ return 'already_resolved';
224
+ }
225
+ if (error.message?.includes('CannotResolveBeforeLockTime') || error.message?.includes('0x179b')) {
226
+ console.log(' ⏰ Cannot resolve - lock time hasn\'t passed yet');
227
+ return 'too_early';
228
+ }
229
+ if (error.message?.includes('InvalidOracle') || error.message?.includes('0x1799')) {
230
+ console.log(' 🔐 Invalid oracle - your wallet is not the authorized oracle for this game');
231
+ return 'invalid_oracle';
232
+ }
233
+
234
+ console.error(` ❌ Error: ${error.message}`);
235
+ throw error;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Update database after resolution
241
+ */
242
+ async function updateDatabase(gameId, winner, homeScore, awayScore) {
243
+ const axios = require('axios');
244
+ const dubsServerUrl = process.env.DUBS_SERVER_URL || 'http://localhost:3001';
245
+
246
+ try {
247
+ console.log(`\n📡 Updating database at ${dubsServerUrl}...`);
248
+
249
+ await axios.post(
250
+ `${dubsServerUrl}/api/games/${gameId}/resolve`,
251
+ {
252
+ winner,
253
+ homeScore,
254
+ awayScore,
255
+ resolvedAt: new Date().toISOString(),
256
+ resolvedBy: 'manual_script'
257
+ },
258
+ {
259
+ timeout: 10000,
260
+ headers: { 'Content-Type': 'application/json' }
261
+ }
262
+ );
263
+
264
+ console.log(' ✅ Database updated');
265
+ } catch (error) {
266
+ console.error(' ⚠️ Failed to update database:', error.message);
267
+ console.log(' You may need to update it manually');
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Main execution
273
+ */
274
+ async function main() {
275
+ const args = process.argv.slice(2);
276
+
277
+ // Parse arguments
278
+ let gameId = null;
279
+ let winner = null;
280
+ let homeScore = null;
281
+ let awayScore = null;
282
+ let skipDb = false;
283
+
284
+ for (let i = 0; i < args.length; i++) {
285
+ if (args[i] === '--game-id' && args[i + 1]) {
286
+ gameId = args[i + 1];
287
+ i++;
288
+ } else if (args[i] === '--winner' && args[i + 1]) {
289
+ winner = args[i + 1].toLowerCase();
290
+ i++;
291
+ } else if (args[i] === '--home-score' && args[i + 1]) {
292
+ homeScore = parseInt(args[i + 1]);
293
+ i++;
294
+ } else if (args[i] === '--away-score' && args[i + 1]) {
295
+ awayScore = parseInt(args[i + 1]);
296
+ i++;
297
+ } else if (args[i] === '--skip-db') {
298
+ skipDb = true;
299
+ } else if (args[i] === '--help' || args[i] === '-h') {
300
+ console.log('Usage:');
301
+ console.log(' node resolve-stuck-game.js --game-id <id> --winner <home|away|tie> [options]');
302
+ console.log('');
303
+ console.log('Options:');
304
+ console.log(' --game-id <id> The game ID from the database (e.g., sport-1766418200917-00ram5nn3)');
305
+ console.log(' --winner <choice> Winner: home, away, or tie (tie = refund all)');
306
+ console.log(' --home-score <n> Final home team score (for database update)');
307
+ console.log(' --away-score <n> Final away team score (for database update)');
308
+ console.log(' --skip-db Skip database update (only resolve on-chain)');
309
+ console.log('');
310
+ console.log('Environment variables:');
311
+ console.log(' ORACLE_WALLET_JSON Oracle private key as JSON array');
312
+ console.log(' ORACLE_WALLET_PATH Path to oracle wallet JSON file');
313
+ console.log(' SOLANA_RPC_URL Solana RPC endpoint');
314
+ console.log(' PROGRAM_ID Solana program ID');
315
+ console.log(' DUBS_SERVER_URL Server URL for database updates');
316
+ console.log('');
317
+ console.log('Example:');
318
+ console.log(' node resolve-stuck-game.js --game-id sport-1766418200917-00ram5nn3 --winner home --home-score 23 --away-score 10');
319
+ process.exit(0);
320
+ }
321
+ }
322
+
323
+ // Validate required arguments
324
+ if (!gameId) {
325
+ console.error('❌ Missing --game-id argument');
326
+ console.log(' Run with --help for usage');
327
+ process.exit(1);
328
+ }
329
+
330
+ if (!winner) {
331
+ console.error('❌ Missing --winner argument');
332
+ console.log(' Run with --help for usage');
333
+ process.exit(1);
334
+ }
335
+
336
+ if (!['home', 'away', 'tie', 'refund'].includes(winner)) {
337
+ console.error(`❌ Invalid winner: ${winner}`);
338
+ console.log(' Must be: home, away, or tie');
339
+ process.exit(1);
340
+ }
341
+
342
+ console.log('📋 Resolution Details:');
343
+ console.log(` Game ID: ${gameId}`);
344
+ console.log(` Winner: ${winner.toUpperCase()}`);
345
+ if (homeScore !== null) console.log(` Home Score: ${homeScore}`);
346
+ if (awayScore !== null) console.log(` Away Score: ${awayScore}`);
347
+ console.log('');
348
+
349
+ // Load oracle wallet
350
+ console.log('🔐 Loading oracle wallet...');
351
+ const oracleWallet = loadOracleWallet();
352
+ console.log(` Oracle: ${oracleWallet.publicKey.toString()}`);
353
+
354
+ // Check oracle balance
355
+ const balance = await connection.getBalance(oracleWallet.publicKey);
356
+ console.log(` Balance: ${balance / 1e9} SOL`);
357
+
358
+ if (balance < 10000000) { // 0.01 SOL
359
+ console.error(' ⚠️ Low balance! May not have enough for transaction fees');
360
+ }
361
+
362
+ // Resolve on-chain
363
+ const result = await resolveGame(gameId, winner, oracleWallet);
364
+
365
+ if (result && result !== 'already_resolved' && result !== 'too_early' && result !== 'invalid_oracle') {
366
+ // Update database if not skipped
367
+ if (!skipDb && homeScore !== null && awayScore !== null) {
368
+ await updateDatabase(gameId, winner, homeScore, awayScore);
369
+ } else if (!skipDb) {
370
+ console.log('\n💡 Tip: Add --home-score and --away-score to also update the database');
371
+ }
372
+ }
373
+
374
+ console.log('\n✅ Done!');
375
+ }
376
+
377
+ main().catch(error => {
378
+ console.error('\n❌ Fatal error:', error.message);
379
+ process.exit(1);
380
+ });
381
+
382
+
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Connection, Keypair, Transaction, PublicKey } = require('@solana/web3.js');
4
+ const axios = require('axios');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const RPC_URL = 'https://api.devnet.solana.com';
9
+ const API_BASE = 'http://localhost:3001';
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('🔧 Resolving stuck round...');
17
+ console.log('Wallet:', wallet.publicKey.toString());
18
+
19
+ const connection = new Connection(RPC_URL, 'confirmed');
20
+
21
+ try {
22
+ const { data } = await axios.post(`${API_BASE}/jackpot/build/resolve`, {
23
+ keeperAddress: wallet.publicKey.toString(),
24
+ });
25
+
26
+ const tx = Transaction.from(Buffer.from(data.transaction, 'base64'));
27
+ tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
28
+ tx.feePayer = wallet.publicKey;
29
+ tx.sign(wallet);
30
+
31
+ const signature = await connection.sendRawTransaction(tx.serialize(), { skipPreflight: false });
32
+ console.log('Signature:', signature);
33
+
34
+ await connection.confirmTransaction(signature);
35
+ console.log('✅ Round resolved!');
36
+ } catch (error) {
37
+ console.error('❌ Error:', error.response?.data || error.message);
38
+ }
39
+ }
40
+
41
+ main();
42
+
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ # Run Connect4 columns migration
3
+ # This adds missing columns to the games table for Connect4 support
4
+
5
+ echo "Running Connect4 columns migration..."
6
+
7
+ # Check if DATABASE_URL is set
8
+ if [ -z "$DATABASE_URL" ]; then
9
+ echo "DATABASE_URL not set, using local dubs_db"
10
+ psql -d dubs_db -f scripts/migrations/008_add_connect4_columns.sql
11
+ else
12
+ echo "Using DATABASE_URL"
13
+ psql "$DATABASE_URL" -f scripts/migrations/008_add_connect4_columns.sql
14
+ fi
15
+
16
+ echo "Done!"
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+
3
+ # Run the mention system migration
4
+ # Usage: ./scripts/run-mention-migration.sh
5
+
6
+ echo "🔄 Running mention system migration..."
7
+
8
+ # Check if DATABASE_URL is set
9
+ if [ -z "$DATABASE_URL" ]; then
10
+ echo "❌ ERROR: DATABASE_URL environment variable is not set"
11
+ echo "Please set it with: export DATABASE_URL=your_database_url"
12
+ exit 1
13
+ fi
14
+
15
+ # Run the migration
16
+ psql "$DATABASE_URL" -f scripts/add_mention_system.sql
17
+
18
+ if [ $? -eq 0 ]; then
19
+ echo "✅ Mention system migration completed successfully!"
20
+ else
21
+ echo "❌ Migration failed. Please check the error messages above."
22
+ exit 1
23
+ fi
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+
3
+ # ========================================
4
+ # Payment System Migration Runner
5
+ # ========================================
6
+ # Runs the payment system database migration
7
+ # Usage: ./scripts/run-payment-migration.sh
8
+
9
+ set -e
10
+
11
+ echo "🚀 Running Payment System Migration..."
12
+ echo ""
13
+
14
+ # Check if DATABASE_URL is set
15
+ if [ -z "$DATABASE_URL" ]; then
16
+ echo "❌ ERROR: DATABASE_URL environment variable is not set"
17
+ echo ""
18
+ echo "Please set it first:"
19
+ echo " export DATABASE_URL='postgresql://user:pass@host:port/dbname'"
20
+ exit 1
21
+ fi
22
+
23
+ echo "📊 Database: ${DATABASE_URL%%@*}@***"
24
+ echo ""
25
+
26
+ # Run the migration
27
+ echo "📝 Executing migration SQL..."
28
+ psql "$DATABASE_URL" -f scripts/add_payment_system.sql
29
+
30
+ echo ""
31
+ echo "✅ Payment system migration completed successfully!"
32
+ echo ""
33
+ echo "New features enabled:"
34
+ echo " • chat_payments table created"
35
+ echo " • Payment notification types added"
36
+ echo " • Indexes created for performance"
37
+ echo ""
38
+ echo "You can now:"
39
+ echo " 1. Send SOL payments via @mention $amount"
40
+ echo " 2. Track payment history"
41
+ echo " 3. Receive payment notifications"
42
+ echo ""
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
@@ -0,0 +1,31 @@
1
+ #!/bin/bash
2
+ # Run preferred_currency migration
3
+
4
+ # Get DATABASE_URL from environment or .env file
5
+ if [ -f .env ]; then
6
+ export $(cat .env | grep DATABASE_URL | xargs)
7
+ fi
8
+
9
+ echo "🔄 Running preferred_currency migration..."
10
+ echo "Database: $DATABASE_URL"
11
+
12
+ psql "$DATABASE_URL" -f scripts/add-preferred-currency.sql
13
+
14
+ if [ $? -eq 0 ]; then
15
+ echo "✅ Migration completed successfully!"
16
+ echo ""
17
+ echo "To verify, run:"
18
+ echo " psql \$DATABASE_URL -c \"\\d users\" | grep preferred"
19
+ else
20
+ echo "❌ Migration failed!"
21
+ exit 1
22
+ fi
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+
3
+ # Run the referral earnings migration
4
+ # Usage: ./scripts/run-referral-earnings-migration.sh
5
+
6
+ echo "💰 Running Referral Earnings Migration..."
7
+ echo ""
8
+
9
+ # Check if DATABASE_URL is set
10
+ if [ -z "$DATABASE_URL" ]; then
11
+ echo "❌ DATABASE_URL environment variable is not set"
12
+ echo " Please set it before running this script:"
13
+ echo " export DATABASE_URL=postgres://user:pass@host:5432/dbname"
14
+ exit 1
15
+ fi
16
+
17
+ # Run the migration
18
+ psql "$DATABASE_URL" -f scripts/migrations/003_referral_earnings.sql
19
+
20
+ if [ $? -eq 0 ]; then
21
+ echo ""
22
+ echo "✅ Migration completed successfully!"
23
+ echo ""
24
+ echo "📊 Verifying tables were created..."
25
+ psql "$DATABASE_URL" -c "SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'referral%' ORDER BY table_name;"
26
+ else
27
+ echo ""
28
+ echo "❌ Migration failed. Check the errors above."
29
+ exit 1
30
+ fi
31
+
32
+
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ # Run Survivor Pool outcome migration
3
+ # This adds winner_type and completed_at columns for tracking pool outcomes
4
+
5
+ echo "Running Survivor Pool outcome migration..."
6
+
7
+ # Check if DATABASE_URL is set
8
+ if [ -z "$DATABASE_URL" ]; then
9
+ echo "DATABASE_URL not set, using local dubs_db"
10
+ psql -d dubs_db -f scripts/migrations/010_add_survivor_pool_outcome.sql
11
+ else
12
+ echo "Using DATABASE_URL"
13
+ psql "$DATABASE_URL" -f scripts/migrations/010_add_survivor_pool_outcome.sql
14
+ fi
15
+
16
+ echo "Done!"