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,369 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * End-to-End Test: Connect4 Create -> Cancel -> Claim
4
+ *
5
+ * This test validates the full flow with the polling confirmation fix.
6
+ *
7
+ * Usage:
8
+ * node test-connect4-e2e.js
9
+ */
10
+
11
+ require('dotenv').config();
12
+ const { Connection, Keypair, Transaction, PublicKey, LAMPORTS_PER_SOL } = require('@solana/web3.js');
13
+ const { pool } = require('../services/db');
14
+ const axios = require('axios');
15
+ const jwt = require('jsonwebtoken');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ // Configuration
20
+ const RPC_URL = process.env.SOLANA_RPC_URL || process.env.SOLANA_NETWORK || 'https://api.devnet.solana.com';
21
+ const SERVER_URL = process.env.TEST_SERVER_URL || 'http://localhost:3001';
22
+ const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
23
+
24
+ console.log('🧪 Connect4 End-to-End Test');
25
+ console.log('===========================');
26
+ console.log(`RPC URL: ${RPC_URL}`);
27
+ console.log(`Server URL: ${SERVER_URL}`);
28
+ console.log('');
29
+
30
+ const connection = new Connection(RPC_URL, 'confirmed');
31
+
32
+ /**
33
+ * Load or create test wallet
34
+ */
35
+ function getTestWallet() {
36
+ const testWalletPath = path.join(__dirname, '../wallets/test-wallet.json');
37
+
38
+ // Try to load existing test wallet
39
+ if (fs.existsSync(testWalletPath)) {
40
+ console.log('šŸ“‚ Loading existing test wallet...');
41
+ const secretKey = JSON.parse(fs.readFileSync(testWalletPath, 'utf-8'));
42
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
43
+ }
44
+
45
+ // Try oracle wallet for testing
46
+ if (process.env.ORACLE_WALLET_JSON) {
47
+ console.log('šŸ“‚ Using oracle wallet for testing...');
48
+ const secretKey = JSON.parse(process.env.ORACLE_WALLET_JSON);
49
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
50
+ }
51
+
52
+ // Try operator wallet
53
+ if (process.env.OPERATOR_WALLET_JSON) {
54
+ console.log('šŸ“‚ Using operator wallet for testing...');
55
+ const secretKey = JSON.parse(process.env.OPERATOR_WALLET_JSON);
56
+ return Keypair.fromSecretKey(Uint8Array.from(secretKey));
57
+ }
58
+
59
+ throw new Error('No test wallet found. Create one at wallets/test-wallet.json');
60
+ }
61
+
62
+ /**
63
+ * Ensure user exists in database
64
+ */
65
+ async function ensureUserExists(walletAddress) {
66
+ const existing = await pool.query(
67
+ 'SELECT wallet_address FROM users WHERE wallet_address = $1',
68
+ [walletAddress]
69
+ );
70
+
71
+ if (existing.rows.length === 0) {
72
+ console.log(` Creating user in database: ${walletAddress.slice(0, 16)}...`);
73
+ await pool.query(
74
+ 'INSERT INTO users (wallet_address, username) VALUES ($1, $2)',
75
+ [walletAddress, `test-${walletAddress.slice(0, 8)}`]
76
+ );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Generate JWT token for testing
82
+ */
83
+ function generateTestToken(walletAddress) {
84
+ return jwt.sign(
85
+ { walletAddress, type: 'auth' },
86
+ JWT_SECRET,
87
+ { expiresIn: '1h' }
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Check wallet balance
93
+ */
94
+ async function checkBalance(walletAddress) {
95
+ const balance = await connection.getBalance(new PublicKey(walletAddress));
96
+ const solBalance = balance / LAMPORTS_PER_SOL;
97
+ console.log(` Balance: ${solBalance.toFixed(4)} SOL`);
98
+ return solBalance;
99
+ }
100
+
101
+ /**
102
+ * Step 1: Create Connect4 game
103
+ */
104
+ async function createGame(wallet, authToken) {
105
+ console.log('\nšŸ“‹ Step 1: Create Connect4 Game');
106
+ console.log('--------------------------------');
107
+
108
+ const walletAddress = wallet.publicKey.toString();
109
+ console.log(` Creator: ${walletAddress.slice(0, 16)}...`);
110
+
111
+ // Build the game transaction
112
+ const response = await axios.post(
113
+ `${SERVER_URL}/api/v1/prod/transaction/build/create-connect4-game`,
114
+ {
115
+ creatorAddress: walletAddress,
116
+ buyIn: 0.01, // Small amount for testing
117
+ },
118
+ { headers: { 'Content-Type': 'application/json' } }
119
+ );
120
+
121
+ if (!response.data.success) {
122
+ throw new Error(`Failed to build game: ${response.data.error}`);
123
+ }
124
+
125
+ const { gameId, gameAddress, transaction: txBase64 } = response.data;
126
+ console.log(` Game ID: ${gameId}`);
127
+ console.log(` Game PDA: ${gameAddress}`);
128
+
129
+ // Deserialize and sign the transaction
130
+ const txBuffer = Buffer.from(txBase64, 'base64');
131
+ const tx = Transaction.from(txBuffer);
132
+ tx.sign(wallet);
133
+
134
+ // Send the signed transaction
135
+ console.log(` Sending transaction...`);
136
+ const signature = await connection.sendRawTransaction(tx.serialize(), {
137
+ skipPreflight: false,
138
+ preflightCommitment: 'confirmed',
139
+ });
140
+ console.log(` Signature: ${signature}`);
141
+
142
+ // Wait for confirmation using polling
143
+ console.log(` Waiting for confirmation (polling)...`);
144
+ await pollConfirmation(signature);
145
+
146
+ // Record the game in the database
147
+ await pool.query(`
148
+ INSERT INTO games (game_id, game_address, game_type, created_by, buy_in, game_status, max_players, game_mode)
149
+ VALUES ($1, $2, 'connect4', $3, $4, 'waiting', 2, 1)
150
+ ON CONFLICT (game_id) DO UPDATE SET game_status = 'waiting'
151
+ `, [gameId, gameAddress, walletAddress, 0.01]);
152
+
153
+ // Record user_game_ref
154
+ await pool.query(`
155
+ INSERT INTO user_game_refs (wallet_address, game_id, role, team_choice, my_signature)
156
+ VALUES ($1, $2, 'creator', 'home', $3)
157
+ ON CONFLICT (wallet_address, game_id) DO NOTHING
158
+ `, [walletAddress, gameId, signature]);
159
+
160
+ console.log(` āœ… Game created successfully!`);
161
+ return { gameId, gameAddress, signature };
162
+ }
163
+
164
+ /**
165
+ * Step 2: Cancel the game
166
+ */
167
+ async function cancelGame(gameId, authToken) {
168
+ console.log('\nšŸ“‹ Step 2: Cancel Game');
169
+ console.log('-----------------------');
170
+ console.log(` Game ID: ${gameId}`);
171
+
172
+ const response = await axios.post(
173
+ `${SERVER_URL}/api/connect4/cancel`,
174
+ { gameId },
175
+ {
176
+ headers: {
177
+ 'Authorization': `Bearer ${authToken}`,
178
+ 'Content-Type': 'application/json'
179
+ },
180
+ timeout: 60000
181
+ }
182
+ );
183
+
184
+ console.log(` Response: ${JSON.stringify(response.data)}`);
185
+
186
+ if (response.data.success) {
187
+ console.log(` āœ… Game cancelled successfully!`);
188
+ if (response.data.signature) {
189
+ console.log(` Signature: ${response.data.signature}`);
190
+ }
191
+ return response.data;
192
+ } else {
193
+ throw new Error(`Cancel failed: ${response.data.error}`);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Step 3: Claim the refund
199
+ */
200
+ async function claimRefund(gameId, authToken) {
201
+ console.log('\nšŸ“‹ Step 3: Claim Refund');
202
+ console.log('------------------------');
203
+ console.log(` Game ID: ${gameId}`);
204
+
205
+ const response = await axios.post(
206
+ `${SERVER_URL}/api/connect4/claim`,
207
+ { gameId },
208
+ {
209
+ headers: {
210
+ 'Authorization': `Bearer ${authToken}`,
211
+ 'Content-Type': 'application/json'
212
+ },
213
+ timeout: 60000
214
+ }
215
+ );
216
+
217
+ console.log(` Response status: ${response.status}`);
218
+
219
+ if (response.data.transaction) {
220
+ console.log(` āœ… Claim transaction built successfully!`);
221
+ console.log(` Transaction ready for user to sign`);
222
+ return { success: true, transaction: response.data.transaction };
223
+ } else if (response.data.success) {
224
+ console.log(` āœ… Claim processed!`);
225
+ return response.data;
226
+ } else {
227
+ console.log(` Response: ${JSON.stringify(response.data)}`);
228
+ throw new Error(`Claim failed: ${response.data.error || 'Unknown error'}`);
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Poll for transaction confirmation (Alchemy-compatible)
234
+ */
235
+ async function pollConfirmation(signature, timeout = 30000) {
236
+ const start = Date.now();
237
+
238
+ while (Date.now() - start < timeout) {
239
+ const response = await connection.getSignatureStatuses([signature], {
240
+ searchTransactionHistory: true
241
+ });
242
+
243
+ const status = response?.value?.[0];
244
+ if (status) {
245
+ if (status.err) {
246
+ throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
247
+ }
248
+ if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized') {
249
+ return status;
250
+ }
251
+ }
252
+
253
+ await new Promise(resolve => setTimeout(resolve, 1000));
254
+ }
255
+
256
+ throw new Error('Transaction confirmation timeout');
257
+ }
258
+
259
+ /**
260
+ * Verify final game state
261
+ */
262
+ async function verifyFinalState(gameId) {
263
+ console.log('\nšŸ“‹ Verifying Final State');
264
+ console.log('-------------------------');
265
+
266
+ const result = await pool.query(`
267
+ SELECT g.game_id, g.game_status, g.is_resolved, g.connect4_winner, g.claim_signature,
268
+ u.wallet_address, u.claimed_at, u.claim_signature as user_claim_sig
269
+ FROM games g
270
+ LEFT JOIN user_game_refs u ON g.game_id = u.game_id AND u.role = 'creator'
271
+ WHERE g.game_id = $1
272
+ `, [gameId]);
273
+
274
+ if (result.rows.length === 0) {
275
+ console.log(` āŒ Game not found`);
276
+ return false;
277
+ }
278
+
279
+ const game = result.rows[0];
280
+ console.log(` game_status: ${game.game_status}`);
281
+ console.log(` is_resolved: ${game.is_resolved}`);
282
+ console.log(` connect4_winner: ${game.connect4_winner || 'null'}`);
283
+ console.log(` claim_signature: ${game.claim_signature || 'null'}`);
284
+
285
+ const passed = game.game_status === 'cancelled' && game.is_resolved === true;
286
+ if (passed) {
287
+ console.log(` āœ… Game state is correct!`);
288
+ } else {
289
+ console.log(` āŒ Game state is incorrect`);
290
+ }
291
+
292
+ return passed;
293
+ }
294
+
295
+ /**
296
+ * Main test execution
297
+ */
298
+ async function main() {
299
+ let passed = 0;
300
+ let failed = 0;
301
+ let gameId = null;
302
+
303
+ try {
304
+ // Setup
305
+ const wallet = getTestWallet();
306
+ const walletAddress = wallet.publicKey.toString();
307
+ console.log(`šŸ”‘ Test wallet: ${walletAddress}`);
308
+
309
+ // Check balance
310
+ const balance = await checkBalance(walletAddress);
311
+ if (balance < 0.02) {
312
+ console.log(`\nāš ļø Wallet balance too low for testing.`);
313
+ console.log(` Need at least 0.02 SOL, have ${balance.toFixed(4)} SOL`);
314
+ console.log(` Airdrop: solana airdrop 1 ${walletAddress} --url devnet`);
315
+ process.exit(1);
316
+ }
317
+
318
+ // Ensure user exists
319
+ await ensureUserExists(walletAddress);
320
+
321
+ // Generate auth token
322
+ const authToken = generateTestToken(walletAddress);
323
+ console.log(`šŸ” Auth token generated`);
324
+
325
+ // Step 1: Create game
326
+ const createResult = await createGame(wallet, authToken);
327
+ gameId = createResult.gameId;
328
+ passed++;
329
+
330
+ // Step 2: Cancel game
331
+ await cancelGame(gameId, authToken);
332
+ passed++;
333
+
334
+ // Step 3: Claim refund
335
+ await claimRefund(gameId, authToken);
336
+ passed++;
337
+
338
+ // Verify final state
339
+ const stateOk = await verifyFinalState(gameId);
340
+ if (stateOk) passed++; else failed++;
341
+
342
+ } catch (error) {
343
+ console.error(`\nāŒ Error: ${error.message}`);
344
+ if (error.response?.data) {
345
+ console.error(` Response: ${JSON.stringify(error.response.data)}`);
346
+ }
347
+ failed++;
348
+ } finally {
349
+ await pool.end();
350
+ }
351
+
352
+ // Summary
353
+ console.log('\n===========================');
354
+ console.log('šŸ“Š Test Summary');
355
+ console.log('===========================');
356
+ console.log(` Passed: ${passed}`);
357
+ console.log(` Failed: ${failed}`);
358
+ console.log('');
359
+
360
+ if (failed === 0) {
361
+ console.log('āœ… All tests passed! The polling confirmation fix works.');
362
+ } else {
363
+ console.log('āŒ Some tests failed. Check the output above.');
364
+ }
365
+
366
+ process.exit(failed > 0 ? 1 : 0);
367
+ }
368
+
369
+ main();