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,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Cohort Count Discrepancy
|
|
3
|
+
*
|
|
4
|
+
* This script investigates why cohort counts don't match total user counts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
require('dotenv').config();
|
|
8
|
+
const { Pool } = require('pg');
|
|
9
|
+
|
|
10
|
+
async function debugCohorts() {
|
|
11
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
12
|
+
|
|
13
|
+
if (!DATABASE_URL) {
|
|
14
|
+
console.error('ā DATABASE_URL not set');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isProduction = DATABASE_URL.includes('amazonaws') || DATABASE_URL.includes('heroku');
|
|
19
|
+
const pool = new Pool({
|
|
20
|
+
connectionString: DATABASE_URL,
|
|
21
|
+
ssl: isProduction ? { rejectUnauthorized: false } : false,
|
|
22
|
+
max: 2,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
console.log('\nš DEBUGGING COHORT COUNT DISCREPANCY\n');
|
|
26
|
+
console.log('=' .repeat(80));
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// 1. Total unique users with registration_completed
|
|
30
|
+
console.log('\n1ļøā£ TOTAL UNIQUE USERS');
|
|
31
|
+
console.log('-'.repeat(80));
|
|
32
|
+
|
|
33
|
+
const totalUsers = await pool.query(`
|
|
34
|
+
SELECT
|
|
35
|
+
COUNT(DISTINCT user_id) as unique_users,
|
|
36
|
+
COUNT(*) as total_events
|
|
37
|
+
FROM audit_logs
|
|
38
|
+
WHERE log_type = 'registration_completed'
|
|
39
|
+
AND user_id IS NOT NULL
|
|
40
|
+
`);
|
|
41
|
+
|
|
42
|
+
console.log(`Total Unique Users: ${totalUsers.rows[0].unique_users}`);
|
|
43
|
+
console.log(`Total registration_completed Events: ${totalUsers.rows[0].total_events}`);
|
|
44
|
+
console.log(`Average Events per User: ${(totalUsers.rows[0].total_events / totalUsers.rows[0].unique_users).toFixed(2)}`);
|
|
45
|
+
|
|
46
|
+
// 2. Users with multiple registration events
|
|
47
|
+
console.log('\n\n2ļøā£ USERS WITH MULTIPLE REGISTRATIONS');
|
|
48
|
+
console.log('-'.repeat(80));
|
|
49
|
+
|
|
50
|
+
const multipleRegs = await pool.query(`
|
|
51
|
+
SELECT
|
|
52
|
+
user_id,
|
|
53
|
+
COUNT(*) as registration_count,
|
|
54
|
+
MIN(created_at) as first_registration,
|
|
55
|
+
MAX(created_at) as last_registration
|
|
56
|
+
FROM audit_logs
|
|
57
|
+
WHERE log_type = 'registration_completed'
|
|
58
|
+
AND user_id IS NOT NULL
|
|
59
|
+
GROUP BY user_id
|
|
60
|
+
HAVING COUNT(*) > 1
|
|
61
|
+
ORDER BY registration_count DESC
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
if (multipleRegs.rows.length > 0) {
|
|
65
|
+
console.log(`\nā ļø Found ${multipleRegs.rows.length} users with multiple registration events:\n`);
|
|
66
|
+
multipleRegs.rows.forEach(row => {
|
|
67
|
+
console.log(`User: ${row.user_id.substring(0, 12)}...`);
|
|
68
|
+
console.log(` Events: ${row.registration_count}`);
|
|
69
|
+
console.log(` First: ${row.first_registration.toISOString()}`);
|
|
70
|
+
console.log(` Last: ${row.last_registration.toISOString()}`);
|
|
71
|
+
console.log('');
|
|
72
|
+
});
|
|
73
|
+
} else {
|
|
74
|
+
console.log('ā
All users have exactly 1 registration event');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 3. Distribution by month
|
|
78
|
+
console.log('\n3ļøā£ MONTHLY DISTRIBUTION (Using MIN per user)');
|
|
79
|
+
console.log('-'.repeat(80));
|
|
80
|
+
|
|
81
|
+
const monthlyDist = await pool.query(`
|
|
82
|
+
WITH first_registration AS (
|
|
83
|
+
SELECT
|
|
84
|
+
user_id,
|
|
85
|
+
MIN(DATE(created_at)) as signup_date
|
|
86
|
+
FROM audit_logs
|
|
87
|
+
WHERE log_type = 'registration_completed'
|
|
88
|
+
AND user_id IS NOT NULL
|
|
89
|
+
GROUP BY user_id
|
|
90
|
+
)
|
|
91
|
+
SELECT
|
|
92
|
+
DATE_TRUNC('month', signup_date) as cohort_month,
|
|
93
|
+
COUNT(*) as unique_users,
|
|
94
|
+
MIN(signup_date) as first_signup,
|
|
95
|
+
MAX(signup_date) as last_signup
|
|
96
|
+
FROM first_registration
|
|
97
|
+
GROUP BY DATE_TRUNC('month', signup_date)
|
|
98
|
+
ORDER BY cohort_month DESC
|
|
99
|
+
`);
|
|
100
|
+
|
|
101
|
+
console.log('\nMonth | Users | First Signup | Last Signup');
|
|
102
|
+
console.log('-'.repeat(60));
|
|
103
|
+
monthlyDist.rows.forEach(row => {
|
|
104
|
+
const month = new Date(row.cohort_month).toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
105
|
+
const first = new Date(row.first_signup).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
106
|
+
const last = new Date(row.last_signup).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
107
|
+
console.log(`${month.padEnd(15)} | ${row.unique_users.toString().padStart(5)} | ${first.padEnd(13)} | ${last}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// 4. What the API returns
|
|
111
|
+
console.log('\n\n4ļøā£ API QUERY RESULT (Monthly Cohorts)');
|
|
112
|
+
console.log('-'.repeat(80));
|
|
113
|
+
|
|
114
|
+
const apiResult = await pool.query(`
|
|
115
|
+
WITH signups AS (
|
|
116
|
+
SELECT
|
|
117
|
+
user_id,
|
|
118
|
+
MIN(DATE(created_at)) as signup_date,
|
|
119
|
+
MIN(metadata->>'referralCode') as referral_code
|
|
120
|
+
FROM audit_logs
|
|
121
|
+
WHERE log_type = 'registration_completed'
|
|
122
|
+
AND user_id IS NOT NULL
|
|
123
|
+
GROUP BY user_id
|
|
124
|
+
),
|
|
125
|
+
cohorts AS (
|
|
126
|
+
SELECT
|
|
127
|
+
DATE_TRUNC('month', signup_date) as cohort_period,
|
|
128
|
+
user_id,
|
|
129
|
+
signup_date,
|
|
130
|
+
CASE
|
|
131
|
+
WHEN referral_code IS NOT NULL AND referral_code != '' THEN 'referral'
|
|
132
|
+
ELSE 'organic'
|
|
133
|
+
END as user_source
|
|
134
|
+
FROM signups
|
|
135
|
+
),
|
|
136
|
+
retention_calc AS (
|
|
137
|
+
SELECT
|
|
138
|
+
cohort_period,
|
|
139
|
+
COUNT(DISTINCT user_id) as total_users
|
|
140
|
+
FROM cohorts
|
|
141
|
+
WHERE 'all' = 'all' OR user_source = 'all'
|
|
142
|
+
GROUP BY cohort_period
|
|
143
|
+
)
|
|
144
|
+
SELECT
|
|
145
|
+
cohort_period,
|
|
146
|
+
total_users
|
|
147
|
+
FROM retention_calc
|
|
148
|
+
WHERE total_users > 0
|
|
149
|
+
ORDER BY cohort_period DESC
|
|
150
|
+
LIMIT 12
|
|
151
|
+
`);
|
|
152
|
+
|
|
153
|
+
console.log('\nCohort Month | Users (What API Returns)');
|
|
154
|
+
console.log('-'.repeat(50));
|
|
155
|
+
apiResult.rows.forEach(row => {
|
|
156
|
+
const month = new Date(row.cohort_period).toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
157
|
+
console.log(`${month.padEnd(15)} | ${row.total_users}`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 5. Date formatting test
|
|
161
|
+
console.log('\n\n5ļøā£ DATE FORMATTING TEST');
|
|
162
|
+
console.log('-'.repeat(80));
|
|
163
|
+
|
|
164
|
+
const dateTest = await pool.query(`
|
|
165
|
+
WITH test_dates AS (
|
|
166
|
+
SELECT DATE_TRUNC('month', DATE '2025-12-15') as cohort_period
|
|
167
|
+
)
|
|
168
|
+
SELECT
|
|
169
|
+
cohort_period,
|
|
170
|
+
cohort_period + INTERVAL '1 month' - INTERVAL '1 day' as month_end,
|
|
171
|
+
TO_CHAR(cohort_period, 'Month YYYY') as formatted_month
|
|
172
|
+
FROM test_dates
|
|
173
|
+
`);
|
|
174
|
+
|
|
175
|
+
console.log(`\nFor December 15, 2025 signup:`);
|
|
176
|
+
console.log(` DATE_TRUNC result: ${dateTest.rows[0].cohort_period.toISOString()}`);
|
|
177
|
+
console.log(` Month end: ${dateTest.rows[0].month_end.toISOString()}`);
|
|
178
|
+
console.log(` Formatted: ${dateTest.rows[0].formatted_month}`);
|
|
179
|
+
|
|
180
|
+
// 6. Check for the "Nov 30, 2025" issue
|
|
181
|
+
console.log('\n\n6ļøā£ INVESTIGATING "Nov 30, 2025" LABEL');
|
|
182
|
+
console.log('-'.repeat(80));
|
|
183
|
+
|
|
184
|
+
const novCheck = await pool.query(`
|
|
185
|
+
SELECT
|
|
186
|
+
DATE(created_at) as signup_date,
|
|
187
|
+
COUNT(*) as count
|
|
188
|
+
FROM audit_logs
|
|
189
|
+
WHERE log_type = 'registration_completed'
|
|
190
|
+
AND user_id IS NOT NULL
|
|
191
|
+
AND DATE(created_at) >= '2025-11-01'
|
|
192
|
+
AND DATE(created_at) < '2025-12-01'
|
|
193
|
+
GROUP BY DATE(created_at)
|
|
194
|
+
ORDER BY signup_date
|
|
195
|
+
`);
|
|
196
|
+
|
|
197
|
+
if (novCheck.rows.length > 0) {
|
|
198
|
+
console.log('\nā ļø Found signups in November 2025:');
|
|
199
|
+
novCheck.rows.forEach(row => {
|
|
200
|
+
console.log(` ${row.signup_date.toISOString().split('T')[0]}: ${row.count} signups`);
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
console.log('ā
No signups found in November 2025');
|
|
204
|
+
console.log(' The "Nov 30, 2025" label is likely a date formatting bug!');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 7. Summary
|
|
208
|
+
console.log('\n\nš SUMMARY');
|
|
209
|
+
console.log('=' .repeat(80));
|
|
210
|
+
|
|
211
|
+
const summary = await pool.query(`
|
|
212
|
+
WITH first_registration AS (
|
|
213
|
+
SELECT
|
|
214
|
+
user_id,
|
|
215
|
+
MIN(DATE(created_at)) as signup_date
|
|
216
|
+
FROM audit_logs
|
|
217
|
+
WHERE log_type = 'registration_completed'
|
|
218
|
+
AND user_id IS NOT NULL
|
|
219
|
+
GROUP BY user_id
|
|
220
|
+
)
|
|
221
|
+
SELECT
|
|
222
|
+
COUNT(*) as total_unique_users,
|
|
223
|
+
MIN(signup_date) as earliest_signup,
|
|
224
|
+
MAX(signup_date) as latest_signup
|
|
225
|
+
FROM first_registration
|
|
226
|
+
`);
|
|
227
|
+
|
|
228
|
+
console.log(`\nā
Total Unique Users: ${summary.rows[0].total_unique_users}`);
|
|
229
|
+
console.log(`š
Date Range: ${summary.rows[0].earliest_signup.toISOString().split('T')[0]} to ${summary.rows[0].latest_signup.toISOString().split('T')[0]}`);
|
|
230
|
+
console.log(`\nš” EXPLANATION:`);
|
|
231
|
+
console.log(` - ${summary.rows[0].total_unique_users} unique users are spread across multiple monthly cohorts`);
|
|
232
|
+
console.log(` - Each cohort only shows users who signed up in that specific month`);
|
|
233
|
+
console.log(` - "December 2025" cohort = users who signed up between Dec 1-31, 2025`);
|
|
234
|
+
console.log(` - If you see 7 in December, the other ${summary.rows[0].total_unique_users - 7} are in other months`);
|
|
235
|
+
console.log(`\nš BUG IDENTIFIED:`);
|
|
236
|
+
console.log(` - "Nov 30, 2025" label is incorrect for December cohort`);
|
|
237
|
+
console.log(` - Should display "December 2025" with date range`);
|
|
238
|
+
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('\nā Error:', error.message);
|
|
241
|
+
console.error(error);
|
|
242
|
+
} finally {
|
|
243
|
+
await pool.end();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
debugCohorts().catch(console.error);
|
|
248
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* š Debug Winner Calculation
|
|
5
|
+
* Shows VRF result and winner calculation step-by-step
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Connection, PublicKey } = require('@solana/web3.js');
|
|
9
|
+
const axios = require('axios');
|
|
10
|
+
|
|
11
|
+
const RPC_URL = 'https://api.devnet.solana.com';
|
|
12
|
+
const API_BASE = 'http://localhost:3001';
|
|
13
|
+
const PROGRAM_ID = new PublicKey('BHidyz25KWkNPdTHgeANzMg25MM2KEiNnG4yE5F46XUz');
|
|
14
|
+
|
|
15
|
+
async function debugWinner() {
|
|
16
|
+
console.log('š Debug Winner Calculation\n');
|
|
17
|
+
|
|
18
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
19
|
+
|
|
20
|
+
// Get entries
|
|
21
|
+
const { data: entriesData } = await axios.get(`${API_BASE}/jackpot/round/50/entries`);
|
|
22
|
+
const entries = entriesData.entries;
|
|
23
|
+
|
|
24
|
+
console.log('š Entries:');
|
|
25
|
+
entries.forEach((entry, i) => {
|
|
26
|
+
console.log(` [${i}] ${entry.player.slice(0, 8)}... weight: ${entry.weight}, cumulative: ${entry.cumulativeTo}`);
|
|
27
|
+
});
|
|
28
|
+
console.log();
|
|
29
|
+
|
|
30
|
+
// Get round account
|
|
31
|
+
const [roundPda] = PublicKey.findProgramAddressSync(
|
|
32
|
+
[Buffer.from('round'), Buffer.from([1,0,0,0,0,0,0,0])],
|
|
33
|
+
PROGRAM_ID
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log('Round PDA:', roundPda.toString());
|
|
37
|
+
|
|
38
|
+
const roundAccount = await connection.getAccountInfo(roundPda);
|
|
39
|
+
if (!roundAccount) {
|
|
40
|
+
console.log('ā Round account not found!');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Parse VRF result (at offset 51)
|
|
45
|
+
const hasVrfResult = roundAccount.data[51];
|
|
46
|
+
console.log('\nVRF Result exists:', hasVrfResult === 1);
|
|
47
|
+
|
|
48
|
+
if (hasVrfResult !== 1) {
|
|
49
|
+
console.log('ā No VRF result available!');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Read u128 VRF result (16 bytes starting at offset 52)
|
|
54
|
+
const vrfBytes = roundAccount.data.slice(52, 68);
|
|
55
|
+
console.log('VRF bytes (hex):', vrfBytes.toString('hex'));
|
|
56
|
+
|
|
57
|
+
const vrfResult = BigInt('0x' + Buffer.from(vrfBytes).reverse().toString('hex'));
|
|
58
|
+
console.log('VRF result (as bigint):', vrfResult.toString());
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
// Calculate winner
|
|
62
|
+
const totalWeight = BigInt(entries[entries.length - 1].cumulativeTo);
|
|
63
|
+
console.log('Total weight:', totalWeight.toString());
|
|
64
|
+
|
|
65
|
+
const winnerPoint = vrfResult % totalWeight;
|
|
66
|
+
console.log('Winner point:', winnerPoint.toString());
|
|
67
|
+
console.log();
|
|
68
|
+
|
|
69
|
+
// Find winner
|
|
70
|
+
console.log('Finding winner:');
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const cumulative = BigInt(entry.cumulativeTo);
|
|
73
|
+
const matches = cumulative > winnerPoint;
|
|
74
|
+
console.log(` ${entry.player.slice(0, 8)}... cumulative: ${cumulative}, matches: ${matches ? 'ā
WINNER' : 'ā'}`);
|
|
75
|
+
if (matches) {
|
|
76
|
+
console.log();
|
|
77
|
+
console.log('š Winner:', entry.player);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
debugWinner().catch(console.error);
|
|
84
|
+
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Dubs Payment System Deployment Script
|
|
4
|
+
# Deploys server with payment functionality to Heroku
|
|
5
|
+
|
|
6
|
+
set -e # Exit on error
|
|
7
|
+
|
|
8
|
+
echo "š Starting Dubs Payment System Deployment..."
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
# Colors for output
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
YELLOW='\033[1;33m'
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
NC='\033[0m' # No Color
|
|
16
|
+
|
|
17
|
+
# Check if we're in the right directory
|
|
18
|
+
if [ ! -f "server.js" ]; then
|
|
19
|
+
echo -e "${RED}ā Error: Must run from dubs-server directory${NC}"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Check if heroku CLI is installed
|
|
24
|
+
if ! command -v heroku &> /dev/null; then
|
|
25
|
+
echo -e "${RED}ā Error: Heroku CLI not installed${NC}"
|
|
26
|
+
echo "Install from: https://devcenter.heroku.com/articles/heroku-cli"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Get Heroku app name
|
|
31
|
+
echo -e "${YELLOW}š Enter your Heroku app name:${NC}"
|
|
32
|
+
read -p "App name: " APP_NAME
|
|
33
|
+
|
|
34
|
+
if [ -z "$APP_NAME" ]; then
|
|
35
|
+
echo -e "${RED}ā Error: App name is required${NC}"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo -e "${GREEN}ā
Deploying to: $APP_NAME${NC}"
|
|
41
|
+
echo ""
|
|
42
|
+
|
|
43
|
+
# Check git status
|
|
44
|
+
echo "š Checking git status..."
|
|
45
|
+
if [[ -n $(git status -s) ]]; then
|
|
46
|
+
echo -e "${YELLOW}ā ļø You have uncommitted changes${NC}"
|
|
47
|
+
echo ""
|
|
48
|
+
git status -s
|
|
49
|
+
echo ""
|
|
50
|
+
read -p "Commit these changes? (y/n): " -n 1 -r
|
|
51
|
+
echo
|
|
52
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
53
|
+
git add .
|
|
54
|
+
read -p "Commit message: " COMMIT_MSG
|
|
55
|
+
git commit -m "$COMMIT_MSG"
|
|
56
|
+
echo -e "${GREEN}ā
Changes committed${NC}"
|
|
57
|
+
else
|
|
58
|
+
echo -e "${RED}ā Deployment cancelled${NC}"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Show what will be deployed
|
|
64
|
+
echo ""
|
|
65
|
+
echo "š¦ Deployment summary:"
|
|
66
|
+
echo " - Payment system with SOL transfers"
|
|
67
|
+
echo " - Payment notifications (payment_received, payment_sent)"
|
|
68
|
+
echo " - Database constraint update (auto-runs on startup)"
|
|
69
|
+
echo " - Real-time WebSocket payment alerts"
|
|
70
|
+
echo ""
|
|
71
|
+
|
|
72
|
+
read -p "Continue with deployment? (y/n): " -n 1 -r
|
|
73
|
+
echo
|
|
74
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
75
|
+
echo -e "${RED}ā Deployment cancelled${NC}"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Deploy to Heroku
|
|
80
|
+
echo ""
|
|
81
|
+
echo "š Deploying to Heroku..."
|
|
82
|
+
git push heroku main
|
|
83
|
+
|
|
84
|
+
# Wait a moment for deployment to settle
|
|
85
|
+
echo ""
|
|
86
|
+
echo "ā³ Waiting for deployment to complete..."
|
|
87
|
+
sleep 5
|
|
88
|
+
|
|
89
|
+
# Check if migration ran
|
|
90
|
+
echo ""
|
|
91
|
+
echo "š Checking database migration..."
|
|
92
|
+
heroku logs --tail --app "$APP_NAME" | grep -i "notification types updated" | head -1 || echo -e "${YELLOW}ā ļø Migration log not found yet (might still be running)${NC}"
|
|
93
|
+
|
|
94
|
+
# Show recent logs
|
|
95
|
+
echo ""
|
|
96
|
+
echo "š Recent logs:"
|
|
97
|
+
heroku logs --tail --num 50 --app "$APP_NAME"
|
|
98
|
+
|
|
99
|
+
echo ""
|
|
100
|
+
echo -e "${GREEN}ā
Deployment complete!${NC}"
|
|
101
|
+
echo ""
|
|
102
|
+
echo "š§Ŗ Next steps:"
|
|
103
|
+
echo " 1. Test payment: @username \$0.01"
|
|
104
|
+
echo " 2. Check logs: heroku logs --tail --app $APP_NAME"
|
|
105
|
+
echo " 3. Monitor database: heroku pg:psql --app $APP_NAME"
|
|
106
|
+
echo ""
|
|
107
|
+
echo "š See PAYMENT_DEPLOYMENT.md for full testing guide"
|
|
108
|
+
echo ""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# š Quick Heroku Deployment Script
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "š Deploying Dubs Server to Heroku"
|
|
8
|
+
echo "==================================="
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
# Check if we're in the right directory
|
|
12
|
+
if [ ! -f "server.js" ]; then
|
|
13
|
+
echo "ā Error: Must run from dubs-server directory"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Check for uncommitted changes
|
|
18
|
+
if ! git diff-index --quiet HEAD --; then
|
|
19
|
+
echo "ā ļø You have uncommitted changes"
|
|
20
|
+
echo ""
|
|
21
|
+
read -p "Commit them now? (y/n) " -n 1 -r
|
|
22
|
+
echo ""
|
|
23
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
24
|
+
git add .
|
|
25
|
+
read -p "Commit message: " commit_msg
|
|
26
|
+
git commit -m "$commit_msg"
|
|
27
|
+
fi
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Deploy to Heroku
|
|
31
|
+
echo "š¤ Pushing to Heroku..."
|
|
32
|
+
git push heroku-dev main
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "āļø Setting environment variables..."
|
|
36
|
+
heroku config:set SOLANA_NETWORK=https://api.devnet.solana.com -a dubs-server-dev
|
|
37
|
+
heroku config:set JACKPOT_PROGRAM_ID=bqoSjTSPLweMuqNG6jy39jmzGyZvZdWjsr4csGfD8F6 -a dubs-server-dev
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo "š Scaling processes..."
|
|
41
|
+
heroku ps:scale web=1 jackpot-keeper=1 oracle=1 -a dubs-server-dev
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "ā³ Waiting for services to start..."
|
|
45
|
+
sleep 10
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "š„ Health check..."
|
|
49
|
+
curl -s https://dubs-server-dev.herokuapp.com/jackpot/health | python3 -m json.tool
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
echo "š Process status:"
|
|
53
|
+
heroku ps -a dubs-server-dev
|
|
54
|
+
|
|
55
|
+
echo ""
|
|
56
|
+
echo "ā
Deployment complete!"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "š Next steps:"
|
|
59
|
+
echo " - View logs: heroku logs --tail -a dubs-server-dev"
|
|
60
|
+
echo " - Check round: curl https://dubs-server-dev.herokuapp.com/jackpot/round/current | jq ."
|
|
61
|
+
echo " - Monitor keeper: heroku logs --tail --ps jackpot-keeper -a dubs-server-dev"
|
|
62
|
+
echo ""
|
|
63
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* š Diagnose Locked Round
|
|
5
|
+
*
|
|
6
|
+
* Checks why a jackpot round is stuck in "Locked" state
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { Connection, PublicKey } = require('@solana/web3.js');
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
|
|
12
|
+
const RPC_URL = 'https://api.devnet.solana.com';
|
|
13
|
+
const API_BASE = 'http://localhost:3001';
|
|
14
|
+
const PROGRAM_ID = new PublicKey('BHidyz25KWkNPdTHgeANzMg25MM2KEiNnG4yE5F46XUz');
|
|
15
|
+
|
|
16
|
+
async function diagnoseRound() {
|
|
17
|
+
console.log('š Diagnosing Jackpot Round State\n');
|
|
18
|
+
|
|
19
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// 1. Get current round from API
|
|
23
|
+
console.log('š” Fetching current round from API...');
|
|
24
|
+
const { data } = await axios.get(`${API_BASE}/jackpot/round/current`);
|
|
25
|
+
|
|
26
|
+
if (!data.round) {
|
|
27
|
+
console.log('ā No active round found!');
|
|
28
|
+
console.log('š” Solution: Run the keeper bot to open a new round');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const round = data.round;
|
|
33
|
+
console.log('ā
Round found:', round.roundId);
|
|
34
|
+
console.log(' Status:', round.status);
|
|
35
|
+
console.log(' Pot:', Number(round.totalPotLamports) / 1e9, 'SOL');
|
|
36
|
+
console.log(' Entry Count:', round.entryCount || 0);
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
// 2. Check if round is locked
|
|
40
|
+
if (round.status !== 'Locked') {
|
|
41
|
+
console.log(`ā
Round is not locked (status: ${round.status})`);
|
|
42
|
+
if (round.status === 'Open') {
|
|
43
|
+
const slotsRemaining = round.timeRemainingSlots;
|
|
44
|
+
const secsRemaining = Math.floor(slotsRemaining * 0.4);
|
|
45
|
+
console.log(`ā³ Round ends in ~${secsRemaining} seconds (${slotsRemaining} slots)`);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('š Round IS LOCKED - investigating...\n');
|
|
51
|
+
|
|
52
|
+
// 3. Fetch raw account data to check VRF result
|
|
53
|
+
console.log('š Checking on-chain state...');
|
|
54
|
+
const [roundPda] = PublicKey.findProgramAddressSync(
|
|
55
|
+
[Buffer.from('round'), Buffer.from([1, 0, 0, 0, 0, 0, 0, 0])],
|
|
56
|
+
PROGRAM_ID
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const accountInfo = await connection.getAccountInfo(roundPda);
|
|
60
|
+
if (!accountInfo) {
|
|
61
|
+
console.log('ā Round account not found on-chain!');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const accountData = accountInfo.data;
|
|
66
|
+
|
|
67
|
+
// Parse critical fields
|
|
68
|
+
const status = accountData[16]; // 0=Open, 1=Locked, 2=Resolved
|
|
69
|
+
const entryCount = accountData.readUInt32LE(68);
|
|
70
|
+
const serverSeedHash = accountData.slice(108, 140); // 32 bytes
|
|
71
|
+
|
|
72
|
+
// Check VRF result (Option<u128>)
|
|
73
|
+
const hasVrfResult = accountData[49]; // Option discriminant (0=None, 1=Some)
|
|
74
|
+
|
|
75
|
+
console.log(' On-chain status:', ['Open', 'Locked', 'Resolved'][status]);
|
|
76
|
+
console.log(' Entry count:', entryCount);
|
|
77
|
+
console.log(' Server seed hash:', serverSeedHash.toString('hex').slice(0, 16) + '...');
|
|
78
|
+
console.log(' Has VRF result:', hasVrfResult === 1 ? 'YES ā
' : 'NO ā');
|
|
79
|
+
console.log();
|
|
80
|
+
|
|
81
|
+
// 4. Diagnose the issue
|
|
82
|
+
if (hasVrfResult === 0) {
|
|
83
|
+
console.log('šÆ DIAGNOSIS: Oracle randomness not consumed yet\n');
|
|
84
|
+
console.log('Why this happens:');
|
|
85
|
+
console.log(' ⢠The keeper bot locked the round (commit phase)');
|
|
86
|
+
console.log(' ⢠But oracle randomness was never revealed (reveal phase)');
|
|
87
|
+
console.log(' ⢠Without VRF result, the round cannot be resolved\n');
|
|
88
|
+
|
|
89
|
+
console.log('š” SOLUTIONS:\n');
|
|
90
|
+
console.log('1. Make sure the keeper bot is running:');
|
|
91
|
+
console.log(' cd /Users/adamdahan/Developer/iheartsolana/solana-programs/dubs-server');
|
|
92
|
+
console.log(' node scripts/jackpot-keeper.js\n');
|
|
93
|
+
|
|
94
|
+
console.log('2. Or manually reveal randomness:');
|
|
95
|
+
console.log(' curl -X POST http://localhost:3001/jackpot/oracle/reveal \\');
|
|
96
|
+
console.log(` -H "Content-Type: application/json" \\`);
|
|
97
|
+
console.log(` -d '{"roundId": "${round.roundId}", "oracleSeed": "0000000000000000000000000000000000000000000000000000000000000001"}'\n`);
|
|
98
|
+
|
|
99
|
+
console.log('3. Check oracle wallet exists:');
|
|
100
|
+
console.log(' ls -la /Users/adamdahan/Developer/iheartsolana/solana-programs/dubs-server/wallets/jackpot_oracle.json\n');
|
|
101
|
+
|
|
102
|
+
} else {
|
|
103
|
+
console.log('šÆ DIAGNOSIS: VRF result exists but round not resolved yet\n');
|
|
104
|
+
console.log('Why this happens:');
|
|
105
|
+
console.log(' ⢠Oracle randomness was consumed successfully');
|
|
106
|
+
console.log(' ⢠But resolve_round was never called');
|
|
107
|
+
console.log(' ⢠The keeper bot may have crashed or stopped\n');
|
|
108
|
+
|
|
109
|
+
console.log('š” SOLUTIONS:\n');
|
|
110
|
+
console.log('1. Restart the keeper bot:');
|
|
111
|
+
console.log(' cd /Users/adamdahan/Developer/iheartsolana/solana-programs/dubs-server');
|
|
112
|
+
console.log(' node scripts/jackpot-keeper.js\n');
|
|
113
|
+
|
|
114
|
+
console.log('2. Or manually resolve:');
|
|
115
|
+
console.log(' # First, calculate winner off-chain, then:');
|
|
116
|
+
console.log(' # This requires knowing the winner address beforehand\n');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 5. Check keeper bot process
|
|
120
|
+
console.log('š¤ Checking keeper bot status...');
|
|
121
|
+
const { exec } = require('child_process');
|
|
122
|
+
exec('ps aux | grep jackpot-keeper', (error, stdout) => {
|
|
123
|
+
if (stdout.includes('node') && stdout.includes('jackpot-keeper')) {
|
|
124
|
+
console.log('ā
Keeper bot appears to be running');
|
|
125
|
+
console.log('š” Check its logs for errors');
|
|
126
|
+
} else {
|
|
127
|
+
console.log('ā Keeper bot is NOT running!');
|
|
128
|
+
console.log('š” This is likely why the round is stuck');
|
|
129
|
+
}
|
|
130
|
+
console.log();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('ā Error:', error.message);
|
|
135
|
+
console.log('\nš” Make sure:');
|
|
136
|
+
console.log(' 1. The server is running (node server.js)');
|
|
137
|
+
console.log(' 2. You\'re connected to devnet');
|
|
138
|
+
console.log(' 3. The program is deployed');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
diagnoseRound().catch(console.error);
|
|
143
|
+
|