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.
- package/.claude/settings.local.json +280 -0
- package/CLAUDE.md +46 -0
- package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
- package/CURRENT_SESSION.md +171 -0
- package/CURRENT_SESSION_DRAW.md +516 -0
- package/MARCH_MADNESS_SURVIVOR.md +254 -0
- package/PANDA.md +166 -0
- package/Procfile +4 -0
- package/README.md +476 -0
- package/controllers/livescoresController.js +376 -0
- package/controllers/pickemController.js +554 -0
- package/controllers/survivorAdminController.js +887 -0
- package/controllers/survivorController.js +623 -0
- package/cron/oracleMonitor.js +77 -0
- package/cron/pickemOracleMonitor.js +73 -0
- package/data/jackpot-history.json +952 -0
- package/data/ncaaTeams.js +406 -0
- package/documentation/API_SECURITY_GUIDE.md +327 -0
- package/documentation/ARCADE_API.md +593 -0
- package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
- package/documentation/ARCADE_QUICKSTART.md +242 -0
- package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
- package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
- package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
- package/documentation/CLAIM_STATUS_FIX.md +67 -0
- package/documentation/CLI_TOOL_GUIDE.md +372 -0
- package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
- package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
- package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
- package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
- package/documentation/DATABASE_QUERIES.md +269 -0
- package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
- package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
- package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
- package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
- package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
- package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
- package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
- package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
- package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
- package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
- package/documentation/HEROKU_DEPLOYMENT.md +134 -0
- package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
- package/documentation/JACKPOT_API.md +521 -0
- package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
- package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
- package/documentation/JWT_QUICK_SETUP.md +268 -0
- package/documentation/JWT_TESTING_GUIDE.md +404 -0
- package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
- package/documentation/KEEPER_SETUP.md +206 -0
- package/documentation/KEEPER_STATE_MACHINE.md +423 -0
- package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
- package/documentation/LOCAL_VOTING_TEST.md +279 -0
- package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
- package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
- package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
- package/documentation/PNL_TRACKING_SETUP.md +189 -0
- package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
- package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
- package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
- package/documentation/QUICK_AUTH_SETUP.md +99 -0
- package/documentation/QUICK_DEPLOY.md +224 -0
- package/documentation/QUICK_FIX.md +114 -0
- package/documentation/QUICK_START.md +152 -0
- package/documentation/REFEREE_MODE_GUIDE.md +392 -0
- package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
- package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
- package/documentation/RUN_MIGRATION_NOW.md +39 -0
- package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
- package/documentation/SETUP_GUIDE.md +184 -0
- package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
- package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
- package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
- package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
- package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
- package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
- package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
- package/ecosystem.config.js +65 -0
- package/env.template +125 -0
- package/middleware/apiKeyAuth.js +136 -0
- package/middleware/authenticate.js +214 -0
- package/middleware/developerUserAuth.js +76 -0
- package/middleware/socketAuth.js +69 -0
- package/package.json +49 -0
- package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
- package/postman/Dubs-API-v1.postman_collection.json +205 -0
- package/postman/Dubs_Developer_API.postman_collection.json +662 -0
- package/postman/QUICKSTART.md +118 -0
- package/postman/QUICK_REFERENCE.md +246 -0
- package/postman/README.md +71 -0
- package/postman/VOTING_API_GUIDE.md +426 -0
- package/refactor/Animations.md +148 -0
- package/refactor/Chat.md +252 -0
- package/routes/actionsRoutes.js +699 -0
- package/routes/adminRoutes.js +370 -0
- package/routes/analyticsRoutes.js +1262 -0
- package/routes/arcadeRoutes.js +557 -0
- package/routes/authRoutes.js +2310 -0
- package/routes/avatarRoutes.js +85 -0
- package/routes/botRoutes.js +211 -0
- package/routes/chatRoutes.js +377 -0
- package/routes/cryptoPriceRoutes.js +105 -0
- package/routes/developerRoutes.js +4201 -0
- package/routes/deviceRoutes.js +214 -0
- package/routes/dmRoutes.js +167 -0
- package/routes/esportsRoutes.js +806 -0
- package/routes/exchangeRateRoutes.js +233 -0
- package/routes/gamesRoutes.js +3028 -0
- package/routes/jackpotRoutes.js +754 -0
- package/routes/keeperMonitoringRoutes.js +156 -0
- package/routes/keeperWebhookRoutes.js +466 -0
- package/routes/livescoresRoutes.js +31 -0
- package/routes/pickemAdminRoutes.js +199 -0
- package/routes/pickemRoutes.js +231 -0
- package/routes/playerStatsRoutes.js +147 -0
- package/routes/portfolioRoutes.js +217 -0
- package/routes/promoRoutes.js +418 -0
- package/routes/referralEarningsRoutes.js +392 -0
- package/routes/socialRoutes.js +459 -0
- package/routes/sportsRoutes.js +1271 -0
- package/routes/survivorAdminRoutes.js +345 -0
- package/routes/survivorRoutes.js +756 -0
- package/routes/uploadRoutes.js +256 -0
- package/routes/userProfileRoutes.js +244 -0
- package/routes/whatsNewRoutes.js +331 -0
- package/scripts/.claude/settings.local.json +15 -0
- package/scripts/README.md +170 -0
- package/scripts/RESTART_EVERYTHING.sh +104 -0
- package/scripts/add-claim-columns.sql +48 -0
- package/scripts/add-crypto-prices-cache.sql +27 -0
- package/scripts/add-exchange-rates-cache.sql +40 -0
- package/scripts/add-game-invite-column.sql +23 -0
- package/scripts/add-game-invite-notification.sql +33 -0
- package/scripts/add-game-invite-telegram-pref.sql +16 -0
- package/scripts/add-game-joined-notification.sql +16 -0
- package/scripts/add-game-joined-pref.js +40 -0
- package/scripts/add-game-joined-preference.sql +6 -0
- package/scripts/add-game-start-notifications.sql +41 -0
- package/scripts/add-notification-flags-to-games.sql +55 -0
- package/scripts/add-pending-game-dismissals.sql +19 -0
- package/scripts/add-preferred-currency.sql +34 -0
- package/scripts/add-winner-columns.js +61 -0
- package/scripts/add_mention_system.sql +53 -0
- package/scripts/add_payment_system.sql +96 -0
- package/scripts/add_sports_event_id_column.sql +22 -0
- package/scripts/analyze-cohort-data-heroku.js +276 -0
- package/scripts/analyze-cohort-data.js +295 -0
- package/scripts/analyze-prod-cohorts.sh +10 -0
- package/scripts/backfill-matchup-images.js +245 -0
- package/scripts/backfill-missing-signatures.js +175 -0
- package/scripts/backfill-referral-earnings.js +202 -0
- package/scripts/check-chat-schema.js +130 -0
- package/scripts/check-db.sh +14 -0
- package/scripts/check_oracle_in_game.js +54 -0
- package/scripts/cleanup-database.js +193 -0
- package/scripts/clear-notification-cache.js +85 -0
- package/scripts/convert-mnemonic.js +50 -0
- package/scripts/create-users-table.sql +44 -0
- package/scripts/debug-cohort-counts.js +248 -0
- package/scripts/debug-winner-calc.js +84 -0
- package/scripts/deploy-payment-system.sh +118 -0
- package/scripts/deploy-to-heroku.sh +63 -0
- package/scripts/diagnose-locked-round.js +143 -0
- package/scripts/dubs-cli.js +720 -0
- package/scripts/dump-account.js +65 -0
- package/scripts/find-vrf-offset.js +48 -0
- package/scripts/fix-chat-notifications-constraint.sql +122 -0
- package/scripts/fix-claim-columns.js +124 -0
- package/scripts/fix-constraint-now.js +44 -0
- package/scripts/fix-lock-timestamps.js +96 -0
- package/scripts/fix-locked-round.sh +126 -0
- package/scripts/fix-missing-badges.sql +91 -0
- package/scripts/fix-payment-notifications.sql +41 -0
- package/scripts/force-new-round.js +55 -0
- package/scripts/force-resolve-and-claim.js +278 -0
- package/scripts/important/README.md +115 -0
- package/scripts/important/authority-force-lock.js +197 -0
- package/scripts/important/authority-resolve-game.js +267 -0
- package/scripts/important/check-game-status.js +373 -0
- package/scripts/important/list-pending-games-by-version.js +270 -0
- package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
- package/scripts/initialize-jackpot.js +111 -0
- package/scripts/jackpot/.claude/settings.local.json +10 -0
- package/scripts/jackpot/force-reset.js +84 -0
- package/scripts/jackpot/initialize-mainnet.js +100 -0
- package/scripts/jackpot/keeper.js +742 -0
- package/scripts/jackpot/status.js +107 -0
- package/scripts/jackpot/update-round-duration.js +143 -0
- package/scripts/keeper-bot.js +112 -0
- package/scripts/list-pending-games.js +131 -0
- package/scripts/migrate-chat-v2.js +127 -0
- package/scripts/migrate-chat-winners.js +84 -0
- package/scripts/migrate-chat.sh +17 -0
- package/scripts/migrate-game-invite.js +83 -0
- package/scripts/migrate-heroku-game-notifications.sh +159 -0
- package/scripts/migrations/001_analytics_tables.sql +422 -0
- package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
- package/scripts/migrations/003_referral_earnings.sql +208 -0
- package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
- package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
- package/scripts/migrations/005_push_notifications.sql +55 -0
- package/scripts/migrations/006_add_draw_team_players.sql +28 -0
- package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
- package/scripts/migrations/007_add_gif_url.sql +8 -0
- package/scripts/migrations/008_add_connect4_columns.sql +139 -0
- package/scripts/migrations/008_add_pool_tracking.sql +22 -0
- package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
- package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
- package/scripts/migrations/011_create_developer_tables.sql +67 -0
- package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
- package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
- package/scripts/migrations/013_add_network_mode.sql +18 -0
- package/scripts/migrations/014_create_developer_app_users.sql +19 -0
- package/scripts/migrations/015_add_ui_config.sql +4 -0
- package/scripts/migrations/016_add_resolution_secret.sql +4 -0
- package/scripts/migrations/017_add_external_game_id.sql +3 -0
- package/scripts/migrations/018_create_pickem_tables.sql +115 -0
- package/scripts/migrations/019_expo_push_tokens.sql +19 -0
- package/scripts/migrations/create_whats_new_tables.sql +88 -0
- package/scripts/migrations/drop_live_games_tables.sql +34 -0
- package/scripts/open-jackpot-round.js +85 -0
- package/scripts/purge-all-data.sh +329 -0
- package/scripts/purge-all-data.sql +142 -0
- package/scripts/purge-heroku-data.sh +149 -0
- package/scripts/purge-heroku-data.sql +62 -0
- package/scripts/rebuild-heroku-database.sh +113 -0
- package/scripts/recover-funds.js +357 -0
- package/scripts/regenerate-epl-images.js +278 -0
- package/scripts/resize-s3-matchup-images.js +374 -0
- package/scripts/resolve-direct.js +88 -0
- package/scripts/resolve-mock-game.js +124 -0
- package/scripts/resolve-pickem-game.js +55 -0
- package/scripts/resolve-round-manual.js +83 -0
- package/scripts/resolve-stuck-game.js +382 -0
- package/scripts/resolve-stuck-round.js +42 -0
- package/scripts/run-connect4-migration.sh +16 -0
- package/scripts/run-mention-migration.sh +32 -0
- package/scripts/run-payment-migration.sh +51 -0
- package/scripts/run-preferred-currency-migration.sh +31 -0
- package/scripts/run-referral-earnings-migration.sh +32 -0
- package/scripts/run-survivor-outcome-migration.sh +16 -0
- package/scripts/seed-test-users.js +346 -0
- package/scripts/setup-auth-tables.js +78 -0
- package/scripts/setup-complete-database.sql +992 -0
- package/scripts/setup-database-fresh.sh +359 -0
- package/scripts/setup-heroku-keeper.sh +48 -0
- package/scripts/setup-keeper-database.js +83 -0
- package/scripts/setup-keeper-state-db.sql +110 -0
- package/scripts/setup-oracle.sh +39 -0
- package/scripts/setup-pnl-tracking.js +111 -0
- package/scripts/start-devnet.sh +14 -0
- package/scripts/test-arcade-devnet.sh +160 -0
- package/scripts/test-arcade-match.sh +109 -0
- package/scripts/test-automatic-mode.sh +239 -0
- package/scripts/test-connect4-cancel-claim.js +370 -0
- package/scripts/test-connect4-e2e.js +369 -0
- package/scripts/test-connect4-resolve.js +369 -0
- package/scripts/test-game-state-endpoint.js +136 -0
- package/scripts/test-invite-notification.js +86 -0
- package/scripts/test-jackpot-api.sh +71 -0
- package/scripts/test-poll-confirmation.js +267 -0
- package/scripts/test-resolve-game.js +271 -0
- package/scripts/test-resolve-signature.js +223 -0
- package/scripts/test-signature-preservation.js +124 -0
- package/scripts/test-state-machine.js +291 -0
- package/scripts/test-webhook-receiver.js +60 -0
- package/scripts/update-notification-constraint.js +52 -0
- package/scripts/verify-account-layout.js +145 -0
- package/scripts/verify-winner-algorithm.js +278 -0
- package/server.js +5259 -0
- package/services/arcadeMatchService.js +763 -0
- package/services/automaticGameOracle.js +1596 -0
- package/services/chatService.js +1612 -0
- package/services/connect4GameService.js +1049 -0
- package/services/connect4NotificationService.js +374 -0
- package/services/cryptoPriceService.js +223 -0
- package/services/customGameResolver.js +260 -0
- package/services/db.js +79 -0
- package/services/directMessageService.js +389 -0
- package/services/discordNotifications.js +160 -0
- package/services/exchangeRateService.js +289 -0
- package/services/expoPushService.js +314 -0
- package/services/gamesCacheService.js +539 -0
- package/services/jackpotHistory.js +331 -0
- package/services/jackpotService.js +856 -0
- package/services/keeperStateService.js +355 -0
- package/services/matchupImageService.js +591 -0
- package/services/notificationCacheService.js +407 -0
- package/services/pickemOracle.js +440 -0
- package/services/playerStatsService.js +389 -0
- package/services/portfolioService.js +555 -0
- package/services/promoService.js +757 -0
- package/services/promoTreasuryService.js +239 -0
- package/services/pushNotifications.js +353 -0
- package/services/redisService.js +422 -0
- package/services/referralEarningsService.js +728 -0
- package/services/s3Service.js +396 -0
- package/services/socialService.js +1202 -0
- package/services/survivorOracle.js +469 -0
- package/services/survivorSimulator.js +475 -0
- package/services/telegramNotifications.js +461 -0
- package/services/userProfileStatsService.js +1185 -0
- package/services/whatsNewService.js +388 -0
- package/utils/urlHelper.js +95 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Check Game Status Script
|
|
4
|
+
* Shows all players, their picks, win/loss status, and claim status
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node scripts/check-game-status.js <gameId>
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* node scripts/check-game-status.js sport-1769329525013-mmekk2qqd
|
|
11
|
+
*
|
|
12
|
+
* Environment:
|
|
13
|
+
* DATABASE_URL - PostgreSQL connection string (uses Heroku if not set)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { Pool } = require('pg');
|
|
17
|
+
|
|
18
|
+
// Get database URL from environment or use Heroku
|
|
19
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
20
|
+
|
|
21
|
+
if (!DATABASE_URL) {
|
|
22
|
+
console.error('❌ DATABASE_URL not set. Run with:');
|
|
23
|
+
console.error(' heroku config:get DATABASE_URL -a dubs-server-prod | xargs -I {} DATABASE_URL={} node scripts/check-game-status.js <gameId>');
|
|
24
|
+
console.error('');
|
|
25
|
+
console.error('Or for a one-liner:');
|
|
26
|
+
console.error(' DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/check-game-status.js <gameId>');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const pool = new Pool({
|
|
31
|
+
connectionString: DATABASE_URL,
|
|
32
|
+
ssl: { rejectUnauthorized: false }
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
async function checkGameStatus(gameId) {
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log('🎮 Game Status Check');
|
|
38
|
+
console.log('====================');
|
|
39
|
+
console.log(`Game ID: ${gameId}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Get game data
|
|
44
|
+
const gameResult = await pool.query(`
|
|
45
|
+
SELECT
|
|
46
|
+
g.game_id,
|
|
47
|
+
g.buy_in,
|
|
48
|
+
g.is_resolved,
|
|
49
|
+
g.home_team_players,
|
|
50
|
+
g.away_team_players,
|
|
51
|
+
g.draw_team_players,
|
|
52
|
+
g.player_amounts,
|
|
53
|
+
g.sports_event->>'strHomeTeam' as home_team,
|
|
54
|
+
g.sports_event->>'strAwayTeam' as away_team,
|
|
55
|
+
g.sports_event->'finalScore'->>'winner' as winner,
|
|
56
|
+
g.sports_event->'finalScore'->>'homeScore' as home_score,
|
|
57
|
+
g.sports_event->'finalScore'->>'awayScore' as away_score
|
|
58
|
+
FROM games g
|
|
59
|
+
WHERE g.game_id = $1
|
|
60
|
+
`, [gameId]);
|
|
61
|
+
|
|
62
|
+
if (gameResult.rows.length === 0) {
|
|
63
|
+
console.error('❌ Game not found');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const game = gameResult.rows[0];
|
|
68
|
+
|
|
69
|
+
// Display game info
|
|
70
|
+
const homeTeam = game.home_team || 'Home';
|
|
71
|
+
const awayTeam = game.away_team || 'Away';
|
|
72
|
+
|
|
73
|
+
console.log('📊 Game Info');
|
|
74
|
+
console.log('------------');
|
|
75
|
+
console.log(`${homeTeam} vs ${awayTeam}`);
|
|
76
|
+
if (game.is_resolved && !game.winner) {
|
|
77
|
+
console.log(`Score: ${game.home_score || 0} - ${game.away_score || 0}`);
|
|
78
|
+
console.log('Result: REFUND 💰 (no competition)');
|
|
79
|
+
} else if (game.winner) {
|
|
80
|
+
console.log(`Score: ${game.home_score} - ${game.away_score}`);
|
|
81
|
+
console.log(`Winner: ${game.winner === 'home' ? homeTeam : game.winner === 'away' ? awayTeam : 'Draw'}`);
|
|
82
|
+
} else {
|
|
83
|
+
console.log('Status: Not resolved yet');
|
|
84
|
+
}
|
|
85
|
+
console.log(`Buy-in: ${parseFloat(game.buy_in).toFixed(4)} SOL`);
|
|
86
|
+
console.log(`Resolved: ${game.is_resolved ? 'Yes ✅' : 'No ⏳'}`);
|
|
87
|
+
console.log('');
|
|
88
|
+
|
|
89
|
+
// Get all participants with their claim status + promo code info
|
|
90
|
+
const participantsResult = await pool.query(`
|
|
91
|
+
SELECT
|
|
92
|
+
ugr.wallet_address,
|
|
93
|
+
u.username,
|
|
94
|
+
ugr.team_choice,
|
|
95
|
+
ugr.claimed_at,
|
|
96
|
+
ugr.claim_signature,
|
|
97
|
+
ugr.amount_claimed,
|
|
98
|
+
pc.code as promo_code
|
|
99
|
+
FROM user_game_refs ugr
|
|
100
|
+
LEFT JOIN users u ON ugr.wallet_address = u.wallet_address
|
|
101
|
+
LEFT JOIN promo_codes pc ON pc.used_by = ugr.wallet_address AND pc.used_in_game = ugr.game_id
|
|
102
|
+
WHERE ugr.game_id = $1
|
|
103
|
+
ORDER BY ugr.team_choice, ugr.joined_at
|
|
104
|
+
`, [gameId]);
|
|
105
|
+
|
|
106
|
+
if (participantsResult.rows.length === 0) {
|
|
107
|
+
console.log('❌ No participants found');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Calculate totals - support both legacy and pari-mutuel
|
|
112
|
+
const totalPlayers = participantsResult.rows.length;
|
|
113
|
+
const buyIn = parseFloat(game.buy_in);
|
|
114
|
+
const playerAmounts = game.player_amounts || {};
|
|
115
|
+
|
|
116
|
+
// Check if pari-mutuel (has player_amounts entries)
|
|
117
|
+
const isPariMutuel = Object.keys(playerAmounts).length > 0;
|
|
118
|
+
|
|
119
|
+
// Get player's bet amount (from player_amounts if pari-mutuel, else buy_in)
|
|
120
|
+
const getPlayerAmount = (wallet) => {
|
|
121
|
+
if (playerAmounts[wallet] !== undefined) {
|
|
122
|
+
return parseFloat(playerAmounts[wallet]);
|
|
123
|
+
}
|
|
124
|
+
return buyIn; // Legacy player
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Calculate actual total pot from all players
|
|
128
|
+
const allPlayers = [
|
|
129
|
+
...(game.home_team_players || []),
|
|
130
|
+
...(game.away_team_players || []),
|
|
131
|
+
...(game.draw_team_players || []),
|
|
132
|
+
];
|
|
133
|
+
const totalPot = allPlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
134
|
+
// 6% total fee (5% platform + 1% oracle), 5% if user has referrer
|
|
135
|
+
const platformFee = totalPot * 0.06;
|
|
136
|
+
const winnerPool = totalPot - platformFee;
|
|
137
|
+
|
|
138
|
+
// Calculate team pools for pari-mutuel payout calculation
|
|
139
|
+
const homePlayers = game.home_team_players || [];
|
|
140
|
+
const awayPlayers = game.away_team_players || [];
|
|
141
|
+
const homeTeamPool = homePlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
142
|
+
const awayTeamPool = awayPlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
143
|
+
|
|
144
|
+
// Determine winners
|
|
145
|
+
const winningTeam = game.winner; // 'home', 'away', or 'draw'
|
|
146
|
+
const winners = participantsResult.rows.filter(p => p.team_choice === winningTeam);
|
|
147
|
+
|
|
148
|
+
// Calculate winner pool by team for pari-mutuel payout calculation
|
|
149
|
+
const winnerTeamPool = winners.reduce((sum, p) => sum + getPlayerAmount(p.wallet_address), 0);
|
|
150
|
+
|
|
151
|
+
console.log('💰 Pot Breakdown');
|
|
152
|
+
console.log('----------------');
|
|
153
|
+
console.log(`Mode: ${isPariMutuel ? 'Pari-Mutuel 🎰' : 'Legacy (equal split)'}`);
|
|
154
|
+
console.log(`Total Players: ${totalPlayers}`);
|
|
155
|
+
console.log(`Total Pot: ${totalPot.toFixed(4)} SOL`);
|
|
156
|
+
console.log(`Platform Fee (6%): ${platformFee.toFixed(4)} SOL`);
|
|
157
|
+
console.log(`Winner Pool: ${winnerPool.toFixed(4)} SOL`);
|
|
158
|
+
|
|
159
|
+
const isRefund = game.is_resolved && !winningTeam;
|
|
160
|
+
|
|
161
|
+
if (isRefund) {
|
|
162
|
+
// Refund scenario — show team breakdown
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(`📊 Team Pools:`);
|
|
165
|
+
console.log(` ${homeTeam}: ${homeTeamPool.toFixed(4)} SOL (${homePlayers.length} players)`);
|
|
166
|
+
console.log(` ${awayTeam}: ${awayTeamPool.toFixed(4)} SOL (${awayPlayers.length} players)`);
|
|
167
|
+
const refundPerPlayer = (totalPot - platformFee) / totalPlayers;
|
|
168
|
+
console.log(`Refund per player: ~${refundPerPlayer.toFixed(4)} SOL (after 6% fee)`);
|
|
169
|
+
} else if (!game.is_resolved) {
|
|
170
|
+
// Show team breakdown for pending games
|
|
171
|
+
console.log('');
|
|
172
|
+
console.log(`📊 Team Pools:`);
|
|
173
|
+
console.log(` ${homeTeam}: ${homeTeamPool.toFixed(4)} SOL (${homePlayers.length} players)`);
|
|
174
|
+
console.log(` ${awayTeam}: ${awayTeamPool.toFixed(4)} SOL (${awayPlayers.length} players)`);
|
|
175
|
+
if (isPariMutuel) {
|
|
176
|
+
// Show potential odds
|
|
177
|
+
const homeOdds = homeTeamPool > 0 ? (winnerPool / homeTeamPool).toFixed(2) : '∞';
|
|
178
|
+
const awayOdds = awayTeamPool > 0 ? (winnerPool / awayTeamPool).toFixed(2) : '∞';
|
|
179
|
+
console.log(` ${homeTeam} odds: ${homeOdds}x | ${awayTeam} odds: ${awayOdds}x`);
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
console.log(`Winners: ${winners.length}`);
|
|
183
|
+
if (isPariMutuel && winners.length > 0) {
|
|
184
|
+
console.log(`Winners' Total Bets: ${winnerTeamPool.toFixed(4)} SOL`);
|
|
185
|
+
console.log(`Payout: ${winnerPool.toFixed(4)} SOL split proportionally by bet size`);
|
|
186
|
+
} else if (winners.length > 0) {
|
|
187
|
+
const perWinnerAmount = winnerPool / winners.length;
|
|
188
|
+
console.log(`Per Winner: ${perWinnerAmount.toFixed(4)} SOL`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
console.log('');
|
|
192
|
+
|
|
193
|
+
// Display participants table
|
|
194
|
+
console.log('👥 Players');
|
|
195
|
+
console.log('----------');
|
|
196
|
+
console.log('');
|
|
197
|
+
|
|
198
|
+
// Table header
|
|
199
|
+
const header = '| Username | Team Picked | Bet | Result | Claimed | Payout |';
|
|
200
|
+
const divider = '|----------------|----------------|----------|----------|----------|--------------|';
|
|
201
|
+
console.log(header);
|
|
202
|
+
console.log(divider);
|
|
203
|
+
|
|
204
|
+
for (const p of participantsResult.rows) {
|
|
205
|
+
const username = (p.username || p.wallet_address.slice(0, 8) + '...').padEnd(14);
|
|
206
|
+
const teamName = p.team_choice === 'home' ? (game.home_team || 'Home') :
|
|
207
|
+
p.team_choice === 'away' ? (game.away_team || 'Away') : 'Draw';
|
|
208
|
+
const teamDisplay = teamName.slice(0, 14).padEnd(14);
|
|
209
|
+
const playerBet = getPlayerAmount(p.wallet_address);
|
|
210
|
+
const betDisplay = playerBet.toFixed(2).padStart(6) + ' ';
|
|
211
|
+
|
|
212
|
+
let result, claimStatus, payout;
|
|
213
|
+
|
|
214
|
+
if (isRefund) {
|
|
215
|
+
// Refund scenario
|
|
216
|
+
if (p.promo_code) {
|
|
217
|
+
// Sponsored player — refund goes to treasury, not player
|
|
218
|
+
result = 'PROMO 🎟️';
|
|
219
|
+
claimStatus = 'N/A ';
|
|
220
|
+
payout = ' → treasury ';
|
|
221
|
+
} else {
|
|
222
|
+
result = 'REFUND 💰';
|
|
223
|
+
claimStatus = p.claimed_at ? 'Yes ✅ ' : 'No ⏳ ';
|
|
224
|
+
const refundAmount = (totalPot - platformFee) / totalPlayers;
|
|
225
|
+
payout = refundAmount.toFixed(4).padStart(10) + ' SOL';
|
|
226
|
+
}
|
|
227
|
+
} else if (!game.is_resolved) {
|
|
228
|
+
// Game not resolved yet - calculate "if win" payout
|
|
229
|
+
result = 'PENDING ⏳';
|
|
230
|
+
claimStatus = 'N/A ';
|
|
231
|
+
|
|
232
|
+
// Calculate expected payout if this player's team wins
|
|
233
|
+
const playerTeamPool = p.team_choice === 'home' ? homeTeamPool : awayTeamPool;
|
|
234
|
+
let expectedIfWin;
|
|
235
|
+
if (isPariMutuel && playerTeamPool > 0) {
|
|
236
|
+
const share = playerBet / playerTeamPool;
|
|
237
|
+
expectedIfWin = winnerPool * share;
|
|
238
|
+
} else {
|
|
239
|
+
// Legacy: equal split among team members
|
|
240
|
+
const teamCount = p.team_choice === 'home' ? homePlayers.length : awayPlayers.length;
|
|
241
|
+
expectedIfWin = teamCount > 0 ? winnerPool / teamCount : 0;
|
|
242
|
+
}
|
|
243
|
+
payout = `~${expectedIfWin.toFixed(4)} SOL`;
|
|
244
|
+
} else {
|
|
245
|
+
const isWinner = p.team_choice === winningTeam;
|
|
246
|
+
result = isWinner ? 'WON ✅ ' : 'LOST ❌ ';
|
|
247
|
+
|
|
248
|
+
if (isWinner) {
|
|
249
|
+
claimStatus = p.claimed_at ? 'Yes ✅ ' : 'No ⏳ ';
|
|
250
|
+
// Always calculate expected payout (don't trust DB amount_claimed which may be wrong)
|
|
251
|
+
let expectedPayout;
|
|
252
|
+
if (isPariMutuel && winnerTeamPool > 0) {
|
|
253
|
+
// Pari-mutuel: proportional payout
|
|
254
|
+
const share = playerBet / winnerTeamPool;
|
|
255
|
+
expectedPayout = winnerPool * share;
|
|
256
|
+
} else {
|
|
257
|
+
// Legacy: equal split
|
|
258
|
+
expectedPayout = winnerPool / winners.length;
|
|
259
|
+
}
|
|
260
|
+
payout = expectedPayout.toFixed(4).padStart(10) + ' SOL';
|
|
261
|
+
} else {
|
|
262
|
+
claimStatus = 'N/A ';
|
|
263
|
+
payout = ' 0 SOL ';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(`| ${username} | ${teamDisplay} | ${betDisplay} | ${result} | ${claimStatus} | ${payout} |`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log('');
|
|
271
|
+
|
|
272
|
+
// Claim transactions section
|
|
273
|
+
const claimedPlayers = participantsResult.rows.filter(p => p.claim_signature);
|
|
274
|
+
if (claimedPlayers.length > 0) {
|
|
275
|
+
console.log('🔗 Claim Transactions');
|
|
276
|
+
console.log('---------------------');
|
|
277
|
+
for (const p of claimedPlayers) {
|
|
278
|
+
const username = p.username || p.wallet_address.slice(0, 8) + '...';
|
|
279
|
+
const dbAmount = p.amount_claimed ? parseFloat(p.amount_claimed) : null;
|
|
280
|
+
const playerBet = getPlayerAmount(p.wallet_address);
|
|
281
|
+
|
|
282
|
+
// Calculate expected payout
|
|
283
|
+
let expectedPayout;
|
|
284
|
+
if (isPariMutuel && winnerTeamPool > 0) {
|
|
285
|
+
const share = playerBet / winnerTeamPool;
|
|
286
|
+
expectedPayout = winnerPool * share;
|
|
287
|
+
} else {
|
|
288
|
+
expectedPayout = winnerPool / winners.length;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const explorerUrl = `https://explorer.solana.com/tx/${p.claim_signature}`;
|
|
292
|
+
|
|
293
|
+
// Check for discrepancy (>1% difference)
|
|
294
|
+
const hasDiscrepancy = dbAmount && Math.abs(dbAmount - expectedPayout) / expectedPayout > 0.01;
|
|
295
|
+
|
|
296
|
+
console.log(`${username}:`);
|
|
297
|
+
console.log(` Expected: ${expectedPayout.toFixed(4)} SOL`);
|
|
298
|
+
if (dbAmount) {
|
|
299
|
+
console.log(` DB Stored: ${dbAmount.toFixed(4)} SOL${hasDiscrepancy ? ' ⚠️ MISMATCH' : ''}`);
|
|
300
|
+
}
|
|
301
|
+
console.log(` Signature: ${p.claim_signature}`);
|
|
302
|
+
console.log(` Explorer: ${explorerUrl}`);
|
|
303
|
+
console.log('');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Summary
|
|
308
|
+
console.log('📋 Summary');
|
|
309
|
+
console.log('----------');
|
|
310
|
+
|
|
311
|
+
if (isRefund) {
|
|
312
|
+
const sponsoredPlayers = participantsResult.rows.filter(p => p.promo_code);
|
|
313
|
+
const regularPlayers = participantsResult.rows.filter(p => !p.promo_code);
|
|
314
|
+
const claimedCount = regularPlayers.filter(p => p.claimed_at).length;
|
|
315
|
+
|
|
316
|
+
console.log(`💰 REFUND — ${regularPlayers.length} player(s) eligible for refund`);
|
|
317
|
+
if (sponsoredPlayers.length > 0) {
|
|
318
|
+
console.log(`🎟️ ${sponsoredPlayers.length} sponsored player(s) — refund goes to treasury`);
|
|
319
|
+
for (const p of sponsoredPlayers) {
|
|
320
|
+
console.log(` - ${p.username || p.wallet_address.slice(0, 8) + '...'} (code: ${p.promo_code})`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
console.log(`Claimed: ${claimedCount}/${regularPlayers.length}`);
|
|
324
|
+
|
|
325
|
+
const unclaimed = regularPlayers.filter(p => !p.claimed_at);
|
|
326
|
+
if (unclaimed.length > 0) {
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log('⏳ Pending refund claims:');
|
|
329
|
+
for (const p of unclaimed) {
|
|
330
|
+
console.log(` - ${p.username || p.wallet_address.slice(0, 8) + '...'}`);
|
|
331
|
+
}
|
|
332
|
+
} else if (regularPlayers.length > 0) {
|
|
333
|
+
console.log('✅ All players have claimed their refund!');
|
|
334
|
+
}
|
|
335
|
+
} else if (!game.is_resolved) {
|
|
336
|
+
console.log('⏳ Game not yet resolved - waiting for final score');
|
|
337
|
+
console.log(` ${totalPlayers} players waiting for result`);
|
|
338
|
+
} else {
|
|
339
|
+
const claimedCount = winners.filter(w => w.claimed_at).length;
|
|
340
|
+
console.log(`Winners claimed: ${claimedCount}/${winners.length}`);
|
|
341
|
+
|
|
342
|
+
if (claimedCount < winners.length) {
|
|
343
|
+
console.log('');
|
|
344
|
+
console.log('⏳ Pending claims:');
|
|
345
|
+
for (const w of winners.filter(w => !w.claimed_at)) {
|
|
346
|
+
console.log(` - ${w.username || w.wallet_address.slice(0, 8) + '...'}`);
|
|
347
|
+
}
|
|
348
|
+
} else if (winners.length > 0) {
|
|
349
|
+
console.log('✅ All winners have claimed!');
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
console.log('');
|
|
354
|
+
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error('❌ Error:', error.message);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
} finally {
|
|
359
|
+
await pool.end();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Main
|
|
364
|
+
const gameId = process.argv[2];
|
|
365
|
+
if (!gameId) {
|
|
366
|
+
console.log('Usage: node scripts/check-game-status.js <gameId>');
|
|
367
|
+
console.log('');
|
|
368
|
+
console.log('Example:');
|
|
369
|
+
console.log(' DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/check-game-status.js sport-1769329525013-mmekk2qqd');
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
checkGameStatus(gameId);
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* List Pending Games by Version (V1 Legacy vs V2 Pari-Mutuel)
|
|
4
|
+
*
|
|
5
|
+
* Shows all pending games in production with full player details:
|
|
6
|
+
* - V1 (Legacy): Equal split payouts, no player_amounts
|
|
7
|
+
* - V2 (Pari-Mutuel): Proportional payouts based on bet amounts
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/list-pending-games-by-version.js
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { Pool } = require('pg');
|
|
14
|
+
|
|
15
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
16
|
+
|
|
17
|
+
if (!DATABASE_URL) {
|
|
18
|
+
console.error('❌ DATABASE_URL not set. Run with:');
|
|
19
|
+
console.error(' DATABASE_URL=$(heroku config:get DATABASE_URL -a dubs-server-prod) node scripts/list-pending-games-by-version.js');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const pool = new Pool({
|
|
24
|
+
connectionString: DATABASE_URL,
|
|
25
|
+
ssl: { rejectUnauthorized: false }
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
async function getGameDetails(game) {
|
|
29
|
+
const playerAmounts = game.player_amounts || {};
|
|
30
|
+
const buyIn = parseFloat(game.buy_in);
|
|
31
|
+
|
|
32
|
+
// Count total players
|
|
33
|
+
const homePlayers = game.home_team_players || [];
|
|
34
|
+
const awayPlayers = game.away_team_players || [];
|
|
35
|
+
const drawPlayers = game.draw_team_players || [];
|
|
36
|
+
const totalPlayerCount = homePlayers.length + awayPlayers.length + drawPlayers.length;
|
|
37
|
+
|
|
38
|
+
// V2 (pure pari-mutuel) = ALL players are in player_amounts
|
|
39
|
+
// V1/Hybrid = some players are NOT in player_amounts (legacy mode)
|
|
40
|
+
const playerAmountsCount = Object.keys(playerAmounts).length;
|
|
41
|
+
const isPariMutuel = totalPlayerCount > 0 && playerAmountsCount === totalPlayerCount;
|
|
42
|
+
|
|
43
|
+
const getPlayerAmount = (wallet) => {
|
|
44
|
+
if (playerAmounts[wallet] !== undefined) {
|
|
45
|
+
return parseFloat(playerAmounts[wallet]);
|
|
46
|
+
}
|
|
47
|
+
return buyIn;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Get participants
|
|
51
|
+
const participantsResult = await pool.query(`
|
|
52
|
+
SELECT
|
|
53
|
+
ugr.wallet_address,
|
|
54
|
+
u.username,
|
|
55
|
+
ugr.team_choice
|
|
56
|
+
FROM user_game_refs ugr
|
|
57
|
+
LEFT JOIN users u ON ugr.wallet_address = u.wallet_address
|
|
58
|
+
WHERE ugr.game_id = $1
|
|
59
|
+
ORDER BY ugr.team_choice, ugr.joined_at
|
|
60
|
+
`, [game.game_id]);
|
|
61
|
+
|
|
62
|
+
// Calculate pools
|
|
63
|
+
const homeTeamPool = homePlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
64
|
+
const awayTeamPool = awayPlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
65
|
+
const drawTeamPool = drawPlayers.reduce((sum, wallet) => sum + getPlayerAmount(wallet), 0);
|
|
66
|
+
const totalPot = homeTeamPool + awayTeamPool + drawTeamPool;
|
|
67
|
+
const platformFee = totalPot * 0.06;
|
|
68
|
+
const winnerPool = totalPot - platformFee;
|
|
69
|
+
|
|
70
|
+
// Calculate odds
|
|
71
|
+
const homeOdds = homeTeamPool > 0 ? (winnerPool / homeTeamPool).toFixed(2) : '∞';
|
|
72
|
+
const awayOdds = awayTeamPool > 0 ? (winnerPool / awayTeamPool).toFixed(2) : '∞';
|
|
73
|
+
const drawOdds = drawTeamPool > 0 ? (winnerPool / drawTeamPool).toFixed(2) : '∞';
|
|
74
|
+
|
|
75
|
+
// Build player details
|
|
76
|
+
const players = participantsResult.rows.map(p => {
|
|
77
|
+
const playerBet = getPlayerAmount(p.wallet_address);
|
|
78
|
+
const playerTeamPool = p.team_choice === 'home' ? homeTeamPool :
|
|
79
|
+
p.team_choice === 'away' ? awayTeamPool : drawTeamPool;
|
|
80
|
+
|
|
81
|
+
let expectedIfWin;
|
|
82
|
+
if (isPariMutuel && playerTeamPool > 0) {
|
|
83
|
+
const share = playerBet / playerTeamPool;
|
|
84
|
+
expectedIfWin = winnerPool * share;
|
|
85
|
+
} else {
|
|
86
|
+
// Legacy: equal split among team members
|
|
87
|
+
const teamCount = p.team_choice === 'home' ? homePlayers.length :
|
|
88
|
+
p.team_choice === 'away' ? awayPlayers.length : drawPlayers.length;
|
|
89
|
+
expectedIfWin = teamCount > 0 ? winnerPool / teamCount : 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
username: p.username || p.wallet_address.slice(0, 8) + '...',
|
|
94
|
+
wallet: p.wallet_address,
|
|
95
|
+
team: p.team_choice,
|
|
96
|
+
teamName: p.team_choice === 'home' ? game.home_team :
|
|
97
|
+
p.team_choice === 'away' ? game.away_team : 'Draw',
|
|
98
|
+
bet: playerBet,
|
|
99
|
+
expectedPayout: expectedIfWin
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
id: game.game_id,
|
|
105
|
+
matchup: `${game.home_team || 'Unknown'} vs ${game.away_team || 'Unknown'}`,
|
|
106
|
+
homeTeam: game.home_team || 'Home',
|
|
107
|
+
awayTeam: game.away_team || 'Away',
|
|
108
|
+
type: game.game_type || 'sports',
|
|
109
|
+
eventTime: game.event_time ? new Date(game.event_time + 'Z').toLocaleString() : 'N/A',
|
|
110
|
+
isPariMutuel,
|
|
111
|
+
buyIn,
|
|
112
|
+
totalPot,
|
|
113
|
+
winnerPool,
|
|
114
|
+
homeTeamPool,
|
|
115
|
+
awayTeamPool,
|
|
116
|
+
drawTeamPool,
|
|
117
|
+
homeOdds,
|
|
118
|
+
awayOdds,
|
|
119
|
+
drawOdds,
|
|
120
|
+
homePlayers: homePlayers.length,
|
|
121
|
+
awayPlayers: awayPlayers.length,
|
|
122
|
+
drawPlayers: drawPlayers.length,
|
|
123
|
+
players
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function printGameDetails(game) {
|
|
128
|
+
const homeTeam = game.homeTeam || 'Home';
|
|
129
|
+
const awayTeam = game.awayTeam || 'Away';
|
|
130
|
+
|
|
131
|
+
console.log(` ┌─────────────────────────────────────────────────────────────────`);
|
|
132
|
+
console.log(` │ 🎯 ${game.id}`);
|
|
133
|
+
console.log(` │ ${game.matchup}`);
|
|
134
|
+
console.log(` │ Type: ${game.type} | Event: ${game.eventTime}`);
|
|
135
|
+
console.log(` │`);
|
|
136
|
+
console.log(` │ 💰 Pot: ${game.totalPot.toFixed(4)} SOL → Winner Pool: ${game.winnerPool.toFixed(4)} SOL (after 6% fee)`);
|
|
137
|
+
console.log(` │`);
|
|
138
|
+
console.log(` │ 📊 Team Pools:`);
|
|
139
|
+
console.log(` │ ${homeTeam}: ${game.homeTeamPool.toFixed(4)} SOL (${game.homePlayers} players) → ${game.homeOdds}x if win`);
|
|
140
|
+
console.log(` │ ${awayTeam}: ${game.awayTeamPool.toFixed(4)} SOL (${game.awayPlayers} players) → ${game.awayOdds}x if win`);
|
|
141
|
+
if (game.drawPlayers > 0) {
|
|
142
|
+
console.log(` │ Draw: ${game.drawTeamPool.toFixed(4)} SOL (${game.drawPlayers} players) → ${game.drawOdds}x if win`);
|
|
143
|
+
}
|
|
144
|
+
console.log(` │`);
|
|
145
|
+
console.log(` │ 👥 Players:`);
|
|
146
|
+
console.log(` │ ┌──────────────────┬────────────────┬──────────┬──────────────┐`);
|
|
147
|
+
console.log(` │ │ Username │ Team │ Bet │ If Win │`);
|
|
148
|
+
console.log(` │ ├──────────────────┼────────────────┼──────────┼──────────────┤`);
|
|
149
|
+
|
|
150
|
+
for (const p of game.players) {
|
|
151
|
+
const username = (p.username || 'Unknown').slice(0, 16).padEnd(16);
|
|
152
|
+
const team = (p.teamName || p.team || 'Unknown').slice(0, 14).padEnd(14);
|
|
153
|
+
const bet = p.bet.toFixed(4).padStart(8);
|
|
154
|
+
const payout = p.expectedPayout.toFixed(4).padStart(10) + ' SOL';
|
|
155
|
+
console.log(` │ │ ${username} │ ${team} │ ${bet} │ ${payout} │`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(` │ └──────────────────┴────────────────┴──────────┴──────────────┘`);
|
|
159
|
+
console.log(` └─────────────────────────────────────────────────────────────────`);
|
|
160
|
+
console.log('');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function listPendingGamesByVersion() {
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log('╔═══════════════════════════════════════════════════════════════════╗');
|
|
166
|
+
console.log('║ 🎮 PENDING GAMES BY VERSION (V1 vs V2) ║');
|
|
167
|
+
console.log('╚═══════════════════════════════════════════════════════════════════╝');
|
|
168
|
+
console.log('');
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Get all pending games
|
|
172
|
+
const result = await pool.query(`
|
|
173
|
+
SELECT
|
|
174
|
+
g.game_id,
|
|
175
|
+
g.buy_in,
|
|
176
|
+
g.player_amounts,
|
|
177
|
+
g.home_team_players,
|
|
178
|
+
g.away_team_players,
|
|
179
|
+
g.draw_team_players,
|
|
180
|
+
g.sports_event->>'strHomeTeam' as home_team,
|
|
181
|
+
g.sports_event->>'strAwayTeam' as away_team,
|
|
182
|
+
g.sports_event->>'strTimestamp' as event_time,
|
|
183
|
+
g.game_type,
|
|
184
|
+
g.created_at
|
|
185
|
+
FROM games g
|
|
186
|
+
WHERE g.is_resolved = false
|
|
187
|
+
ORDER BY g.created_at DESC
|
|
188
|
+
`);
|
|
189
|
+
|
|
190
|
+
if (result.rows.length === 0) {
|
|
191
|
+
console.log('✅ No pending games found');
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const v1Games = [];
|
|
196
|
+
const v2Games = [];
|
|
197
|
+
|
|
198
|
+
// Process all games
|
|
199
|
+
for (const game of result.rows) {
|
|
200
|
+
const details = await getGameDetails(game);
|
|
201
|
+
if (details.isPariMutuel) {
|
|
202
|
+
v2Games.push(details);
|
|
203
|
+
} else {
|
|
204
|
+
v1Games.push(details);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Display V1 (Legacy/Hybrid) games
|
|
209
|
+
console.log('┌───────────────────────────────────────────────────────────────────┐');
|
|
210
|
+
console.log('│ 📦 V1 (LEGACY/HYBRID) - Equal Split Payouts │');
|
|
211
|
+
console.log('│ Detection: player_amounts.length != total_players │');
|
|
212
|
+
console.log('└───────────────────────────────────────────────────────────────────┘');
|
|
213
|
+
console.log('');
|
|
214
|
+
|
|
215
|
+
if (v1Games.length === 0) {
|
|
216
|
+
console.log(' (none)');
|
|
217
|
+
console.log('');
|
|
218
|
+
} else {
|
|
219
|
+
console.log(` Total: ${v1Games.length} games`);
|
|
220
|
+
console.log('');
|
|
221
|
+
for (const game of v1Games) {
|
|
222
|
+
printGameDetails(game);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Display V2 (Pari-Mutuel) games
|
|
227
|
+
console.log('┌───────────────────────────────────────────────────────────────────┐');
|
|
228
|
+
console.log('│ 🎰 V2 (PARI-MUTUEL) - Proportional Payouts │');
|
|
229
|
+
console.log('│ Detection: player_amounts has entries │');
|
|
230
|
+
console.log('└───────────────────────────────────────────────────────────────────┘');
|
|
231
|
+
console.log('');
|
|
232
|
+
|
|
233
|
+
if (v2Games.length === 0) {
|
|
234
|
+
console.log(' (none)');
|
|
235
|
+
console.log('');
|
|
236
|
+
} else {
|
|
237
|
+
console.log(` Total: ${v2Games.length} games`);
|
|
238
|
+
console.log('');
|
|
239
|
+
for (const game of v2Games) {
|
|
240
|
+
printGameDetails(game);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Summary
|
|
245
|
+
console.log('╔═══════════════════════════════════════════════════════════════════╗');
|
|
246
|
+
console.log('║ 📊 SUMMARY ║');
|
|
247
|
+
console.log('╠═══════════════════════════════════════════════════════════════════╣');
|
|
248
|
+
|
|
249
|
+
const v1Pot = v1Games.reduce((sum, g) => sum + g.totalPot, 0);
|
|
250
|
+
const v2Pot = v2Games.reduce((sum, g) => sum + g.totalPot, 0);
|
|
251
|
+
const v1Players = v1Games.reduce((sum, g) => sum + g.players.length, 0);
|
|
252
|
+
const v2Players = v2Games.reduce((sum, g) => sum + g.players.length, 0);
|
|
253
|
+
|
|
254
|
+
console.log(`║ V1 (Legacy/Hybrid): ${String(v1Games.length).padStart(3)} games | ${String(v1Players).padStart(4)} players | ${v1Pot.toFixed(4).padStart(10)} SOL ║`);
|
|
255
|
+
console.log(`║ V2 (Pari-Mutuel): ${String(v2Games.length).padStart(3)} games | ${String(v2Players).padStart(4)} players | ${v2Pot.toFixed(4).padStart(10)} SOL ║`);
|
|
256
|
+
console.log(`║ ───────────────────────────────────────────────────────────── ║`);
|
|
257
|
+
console.log(`║ TOTAL: ${String(result.rows.length).padStart(3)} games | ${String(v1Players + v2Players).padStart(4)} players | ${(v1Pot + v2Pot).toFixed(4).padStart(10)} SOL ║`);
|
|
258
|
+
console.log('╚═══════════════════════════════════════════════════════════════════╝');
|
|
259
|
+
console.log('');
|
|
260
|
+
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('❌ Error:', error.message);
|
|
263
|
+
console.error(error.stack);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
} finally {
|
|
266
|
+
await pool.end();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
listPendingGamesByVersion();
|