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,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test Script: Poll Transaction Confirmation
|
|
4
|
+
*
|
|
5
|
+
* Tests the Alchemy-compatible polling confirmation approach
|
|
6
|
+
* that uses getSignatureStatuses instead of WebSocket subscriptions.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node test-poll-confirmation.js # Test with a known confirmed tx
|
|
10
|
+
* node test-poll-confirmation.js <signature> # Test with specific signature
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
const { Connection } = require('@solana/web3.js');
|
|
15
|
+
|
|
16
|
+
// Configuration
|
|
17
|
+
const RPC_URL = process.env.SOLANA_RPC_URL || process.env.SOLANA_NETWORK || 'https://api.devnet.solana.com';
|
|
18
|
+
|
|
19
|
+
console.log('🧪 Poll Transaction Confirmation Test');
|
|
20
|
+
console.log('=====================================');
|
|
21
|
+
console.log(`RPC URL: ${RPC_URL}`);
|
|
22
|
+
console.log('');
|
|
23
|
+
|
|
24
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Poll for transaction confirmation using getSignatureStatuses
|
|
28
|
+
* This is the Alchemy-compatible approach (no WebSocket subscriptions)
|
|
29
|
+
*/
|
|
30
|
+
async function pollTransactionConfirmation(signature, lastValidBlockHeight = null, timeout = 30000) {
|
|
31
|
+
const start = Date.now();
|
|
32
|
+
console.log(`🔄 Polling confirmation for: ${signature}`);
|
|
33
|
+
if (lastValidBlockHeight) {
|
|
34
|
+
console.log(` lastValidBlockHeight: ${lastValidBlockHeight}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let pollCount = 0;
|
|
38
|
+
while (Date.now() - start < timeout) {
|
|
39
|
+
pollCount++;
|
|
40
|
+
try {
|
|
41
|
+
// Check if blockhash has expired (if we have lastValidBlockHeight)
|
|
42
|
+
if (lastValidBlockHeight) {
|
|
43
|
+
const currentBlockHeight = await connection.getBlockHeight('confirmed');
|
|
44
|
+
console.log(` [Poll ${pollCount}] Current block height: ${currentBlockHeight}`);
|
|
45
|
+
if (currentBlockHeight > lastValidBlockHeight) {
|
|
46
|
+
throw new Error(`Transaction expired: blockhash no longer valid (current: ${currentBlockHeight}, lastValid: ${lastValidBlockHeight})`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Poll signature status
|
|
51
|
+
const response = await connection.getSignatureStatuses([signature], {
|
|
52
|
+
searchTransactionHistory: true
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const status = response?.value?.[0];
|
|
56
|
+
if (status) {
|
|
57
|
+
console.log(` [Poll ${pollCount}] Status: ${JSON.stringify(status)}`);
|
|
58
|
+
|
|
59
|
+
// Check for error
|
|
60
|
+
if (status.err) {
|
|
61
|
+
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check confirmation level
|
|
65
|
+
if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized') {
|
|
66
|
+
console.log(`✅ Transaction confirmed: ${status.confirmationStatus}`);
|
|
67
|
+
return status;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log(` [Poll ${pollCount}] Status: null (not found yet)`);
|
|
71
|
+
}
|
|
72
|
+
} catch (pollErr) {
|
|
73
|
+
// If it's a fatal error, rethrow
|
|
74
|
+
if (pollErr.message?.includes('expired') || pollErr.message?.includes('failed')) {
|
|
75
|
+
throw pollErr;
|
|
76
|
+
}
|
|
77
|
+
console.warn(` [Poll ${pollCount}] Error (will retry): ${pollErr.message}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Wait before next poll
|
|
81
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Test 1: Confirm a known finalized transaction
|
|
89
|
+
*/
|
|
90
|
+
async function testKnownTransaction(signature) {
|
|
91
|
+
console.log('\n📋 Test 1: Confirm known transaction');
|
|
92
|
+
console.log('------------------------------------');
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const status = await pollTransactionConfirmation(signature, null, 10000);
|
|
96
|
+
console.log(`\n✅ TEST PASSED: Transaction confirmed`);
|
|
97
|
+
console.log(` Slot: ${status.slot}`);
|
|
98
|
+
console.log(` Confirmations: ${status.confirmations}`);
|
|
99
|
+
console.log(` Status: ${status.confirmationStatus}`);
|
|
100
|
+
return true;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`\n❌ TEST FAILED: ${error.message}`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Test 2: Verify getSignatureStatuses works with searchTransactionHistory
|
|
109
|
+
*/
|
|
110
|
+
async function testSearchTransactionHistory(signature) {
|
|
111
|
+
console.log('\n📋 Test 2: searchTransactionHistory parameter');
|
|
112
|
+
console.log('---------------------------------------------');
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Without searchTransactionHistory (might return null for old txs)
|
|
116
|
+
console.log(' Testing WITHOUT searchTransactionHistory...');
|
|
117
|
+
const responseWithout = await connection.getSignatureStatuses([signature]);
|
|
118
|
+
console.log(` Result: ${responseWithout?.value?.[0] ? 'Found' : 'Not found (null)'}`);
|
|
119
|
+
|
|
120
|
+
// With searchTransactionHistory
|
|
121
|
+
console.log(' Testing WITH searchTransactionHistory...');
|
|
122
|
+
const responseWith = await connection.getSignatureStatuses([signature], {
|
|
123
|
+
searchTransactionHistory: true
|
|
124
|
+
});
|
|
125
|
+
console.log(` Result: ${responseWith?.value?.[0] ? 'Found' : 'Not found (null)'}`);
|
|
126
|
+
|
|
127
|
+
if (responseWith?.value?.[0]) {
|
|
128
|
+
console.log(`\n✅ TEST PASSED: searchTransactionHistory works`);
|
|
129
|
+
return true;
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`\n⚠️ TEST INCONCLUSIVE: Transaction not found (may be too old or invalid)`);
|
|
132
|
+
return true; // Not a failure, just inconclusive
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`\n❌ TEST FAILED: ${error.message}`);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Test 3: Verify getBlockHeight works
|
|
142
|
+
*/
|
|
143
|
+
async function testGetBlockHeight() {
|
|
144
|
+
console.log('\n📋 Test 3: getBlockHeight functionality');
|
|
145
|
+
console.log('---------------------------------------');
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const blockHeight = await connection.getBlockHeight('confirmed');
|
|
149
|
+
console.log(` Current block height: ${blockHeight}`);
|
|
150
|
+
|
|
151
|
+
// Get latest blockhash to compare
|
|
152
|
+
const { lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
153
|
+
console.log(` lastValidBlockHeight: ${lastValidBlockHeight}`);
|
|
154
|
+
console.log(` Difference: ${lastValidBlockHeight - blockHeight} blocks`);
|
|
155
|
+
|
|
156
|
+
if (blockHeight > 0 && lastValidBlockHeight > blockHeight) {
|
|
157
|
+
console.log(`\n✅ TEST PASSED: getBlockHeight works correctly`);
|
|
158
|
+
return true;
|
|
159
|
+
} else {
|
|
160
|
+
console.log(`\n❌ TEST FAILED: Unexpected block height values`);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(`\n❌ TEST FAILED: ${error.message}`);
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Test 4: Verify error handling for invalid signature
|
|
171
|
+
*/
|
|
172
|
+
async function testInvalidSignature() {
|
|
173
|
+
console.log('\n📋 Test 4: Invalid signature handling');
|
|
174
|
+
console.log('-------------------------------------');
|
|
175
|
+
|
|
176
|
+
const invalidSig = 'InvalidSignature123456789012345678901234567890123456789012345678901234567890';
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const response = await connection.getSignatureStatuses([invalidSig], {
|
|
180
|
+
searchTransactionHistory: true
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (response?.value?.[0] === null) {
|
|
184
|
+
console.log(` Response for invalid sig: null (as expected)`);
|
|
185
|
+
console.log(`\n✅ TEST PASSED: Invalid signatures return null`);
|
|
186
|
+
return true;
|
|
187
|
+
} else {
|
|
188
|
+
console.log(` Unexpected response: ${JSON.stringify(response)}`);
|
|
189
|
+
console.log(`\n⚠️ TEST INCONCLUSIVE`);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
// Some errors are expected for truly malformed signatures
|
|
194
|
+
console.log(` Error (expected): ${error.message}`);
|
|
195
|
+
console.log(`\n✅ TEST PASSED: Invalid signatures handled gracefully`);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Main execution
|
|
202
|
+
*/
|
|
203
|
+
async function main() {
|
|
204
|
+
const args = process.argv.slice(2);
|
|
205
|
+
|
|
206
|
+
// Use provided signature or a known devnet transaction
|
|
207
|
+
// This is a sample devnet transaction - replace with a real one if needed
|
|
208
|
+
let testSignature = args[0];
|
|
209
|
+
|
|
210
|
+
if (!testSignature) {
|
|
211
|
+
// Try to get a recent transaction from the connection
|
|
212
|
+
console.log('No signature provided, fetching a recent confirmed transaction...');
|
|
213
|
+
try {
|
|
214
|
+
const signatures = await connection.getSignaturesForAddress(
|
|
215
|
+
connection._rpcEndpoint.includes('mainnet')
|
|
216
|
+
? require('@solana/web3.js').SystemProgram.programId
|
|
217
|
+
: require('@solana/web3.js').SystemProgram.programId,
|
|
218
|
+
{ limit: 1 }
|
|
219
|
+
);
|
|
220
|
+
if (signatures.length > 0) {
|
|
221
|
+
testSignature = signatures[0].signature;
|
|
222
|
+
console.log(`Using recent transaction: ${testSignature}\n`);
|
|
223
|
+
}
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.log('Could not fetch recent transaction, using fallback...');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!testSignature) {
|
|
230
|
+
console.log('\n⚠️ No test signature available.');
|
|
231
|
+
console.log(' Please provide a signature: node test-poll-confirmation.js <signature>');
|
|
232
|
+
console.log(' Running remaining tests without transaction confirmation...\n');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let passed = 0;
|
|
236
|
+
let failed = 0;
|
|
237
|
+
|
|
238
|
+
// Run tests
|
|
239
|
+
if (testSignature) {
|
|
240
|
+
if (await testKnownTransaction(testSignature)) passed++; else failed++;
|
|
241
|
+
if (await testSearchTransactionHistory(testSignature)) passed++; else failed++;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (await testGetBlockHeight()) passed++; else failed++;
|
|
245
|
+
if (await testInvalidSignature()) passed++; else failed++;
|
|
246
|
+
|
|
247
|
+
// Summary
|
|
248
|
+
console.log('\n=====================================');
|
|
249
|
+
console.log('📊 Test Summary');
|
|
250
|
+
console.log('=====================================');
|
|
251
|
+
console.log(` Passed: ${passed}`);
|
|
252
|
+
console.log(` Failed: ${failed}`);
|
|
253
|
+
console.log('');
|
|
254
|
+
|
|
255
|
+
if (failed === 0) {
|
|
256
|
+
console.log('✅ All tests passed! The polling confirmation approach works with your RPC.');
|
|
257
|
+
} else {
|
|
258
|
+
console.log('❌ Some tests failed. Check the output above for details.');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
main().catch(err => {
|
|
265
|
+
console.error('Fatal error:', err);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Manual Game Resolution Test Script
|
|
4
|
+
* Use this to manually resolve games and test the notification system
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node test-resolve-game.js <gameId> # Resolve specific game
|
|
8
|
+
* node test-resolve-game.js --all # Resolve all pending games
|
|
9
|
+
* node test-resolve-game.js <gameId> --winner=home # Force winner
|
|
10
|
+
* node test-resolve-game.js <gameId> --score=3:2 # Force score
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
const { Keypair } = require('@solana/web3.js');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const axios = require('axios');
|
|
18
|
+
const AutomaticGameOracle = require('../services/automaticGameOracle');
|
|
19
|
+
|
|
20
|
+
// Parse command line arguments
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const gameIdArg = args[0];
|
|
23
|
+
const options = {};
|
|
24
|
+
|
|
25
|
+
args.forEach(arg => {
|
|
26
|
+
if (arg.startsWith('--winner=')) {
|
|
27
|
+
options.forceWinner = arg.split('=')[1]; // 'home' or 'away'
|
|
28
|
+
}
|
|
29
|
+
if (arg.startsWith('--score=')) {
|
|
30
|
+
const [home, away] = arg.split('=')[1].split(':');
|
|
31
|
+
options.forceScore = { homeScore: parseInt(home), awayScore: parseInt(away) };
|
|
32
|
+
}
|
|
33
|
+
if (arg === '--all') {
|
|
34
|
+
options.resolveAll = true;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Load oracle wallet
|
|
39
|
+
function loadOracleWallet() {
|
|
40
|
+
if (process.env.ORACLE_WALLET_JSON) {
|
|
41
|
+
console.log('🔑 Loading oracle wallet from environment variable');
|
|
42
|
+
const secretKey = JSON.parse(process.env.ORACLE_WALLET_JSON);
|
|
43
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const oracleWalletPath = process.env.ORACLE_WALLET_PATH || path.join(__dirname, 'wallets/oracle.json');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(oracleWalletPath)) {
|
|
49
|
+
console.error(`❌ Oracle wallet not found at: ${oracleWalletPath}`);
|
|
50
|
+
console.log('💡 Create one with: solana-keygen new -o wallets/oracle.json');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log('🔑 Loading oracle wallet from file:', oracleWalletPath);
|
|
55
|
+
const secretKey = JSON.parse(fs.readFileSync(oracleWalletPath, 'utf-8'));
|
|
56
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Configuration
|
|
60
|
+
const config = {
|
|
61
|
+
rpcUrl: process.env.SOLANA_NETWORK || 'https://api.devnet.solana.com',
|
|
62
|
+
programId: process.env.PROGRAM_ID || '8DJTkgk6MDr6tPtw4v2VzYAz9WWvmCg6786vZrEK3o5q',
|
|
63
|
+
oracleKeypair: loadOracleWallet(),
|
|
64
|
+
liveScoresApiUrl: process.env.LIVE_SCORES_API_URL || 'http://localhost:3000',
|
|
65
|
+
dubsGamesApiUrl: process.env.DUBS_GAMES_API_URL || 'http://localhost:3001',
|
|
66
|
+
checkIntervalMs: 60000,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
console.log('🧪 Manual Game Resolution Test');
|
|
70
|
+
console.log('================================');
|
|
71
|
+
console.log(`RPC: ${config.rpcUrl}`);
|
|
72
|
+
console.log(`Program ID: ${config.programId}`);
|
|
73
|
+
console.log(`Oracle: ${config.oracleKeypair.publicKey.toString()}`);
|
|
74
|
+
console.log('');
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fetch a specific game from Firebase
|
|
78
|
+
*/
|
|
79
|
+
async function fetchGame(gameId) {
|
|
80
|
+
try {
|
|
81
|
+
const apiUrl = process.env.DUBS_GAMES_API_URL || 'https://dubs-games-api-dev-76c556653fa3.herokuapp.com';
|
|
82
|
+
const response = await axios.get(`${apiUrl}/api/games/${gameId}`);
|
|
83
|
+
|
|
84
|
+
if (!response.data || !response.data.gameId) {
|
|
85
|
+
throw new Error('Game not found in Firebase');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response.data;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw new Error(`Failed to fetch game: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Fetch all pending games
|
|
96
|
+
*/
|
|
97
|
+
async function fetchPendingGames() {
|
|
98
|
+
try {
|
|
99
|
+
const apiUrl = process.env.DUBS_GAMES_API_URL || 'https://dubs-games-api-dev-76c556653fa3.herokuapp.com';
|
|
100
|
+
const response = await axios.get(`${apiUrl}/api/auth/games/automatic/pending`);
|
|
101
|
+
|
|
102
|
+
if (!response.data.success || !response.data.games) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return response.data.games;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new Error(`Failed to fetch pending games: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get live score or use forced values
|
|
114
|
+
*/
|
|
115
|
+
async function getGameResult(game, options) {
|
|
116
|
+
// If forcing winner/score, use that
|
|
117
|
+
if (options.forceWinner) {
|
|
118
|
+
const score = options.forceScore || { homeScore: 3, awayScore: 2 };
|
|
119
|
+
|
|
120
|
+
// Make sure winner matches score
|
|
121
|
+
if (options.forceWinner === 'home' && score.homeScore <= score.awayScore) {
|
|
122
|
+
score.homeScore = score.awayScore + 1;
|
|
123
|
+
} else if (options.forceWinner === 'away' && score.awayScore <= score.homeScore) {
|
|
124
|
+
score.awayScore = score.homeScore + 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
isFinal: true,
|
|
129
|
+
status: 'Final',
|
|
130
|
+
winner: options.forceWinner,
|
|
131
|
+
homeScore: score.homeScore,
|
|
132
|
+
awayScore: score.awayScore
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Otherwise, try to fetch real score
|
|
137
|
+
const oracle = new AutomaticGameOracle(config);
|
|
138
|
+
const result = await oracle.checkSportsGameResult(game.sportsEvent);
|
|
139
|
+
|
|
140
|
+
if (!result || !result.isFinal) {
|
|
141
|
+
throw new Error('Game not finished yet. Use --winner=home or --winner=away to force resolution.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Resolve a specific game
|
|
149
|
+
*/
|
|
150
|
+
async function resolveGame(gameId, options) {
|
|
151
|
+
try {
|
|
152
|
+
console.log(`\n📊 Fetching game: ${gameId}`);
|
|
153
|
+
const game = await fetchGame(gameId);
|
|
154
|
+
|
|
155
|
+
console.log(` Title: ${game.title || 'Untitled'}`);
|
|
156
|
+
console.log(` Event: ${game.sportsEvent?.strEvent || 'N/A'}`);
|
|
157
|
+
console.log(` Participants: ${game.participants?.length || 0}`);
|
|
158
|
+
console.log(` Chat ID: ${game.telegramChatId || 'NOT SET'}`);
|
|
159
|
+
|
|
160
|
+
if (!game.telegramChatId) {
|
|
161
|
+
console.log(` ⚠️ WARNING: No chat ID - notifications won't be sent!`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(`\n🔍 Getting game result...`);
|
|
165
|
+
const result = await getGameResult(game, options);
|
|
166
|
+
|
|
167
|
+
console.log(` Status: ${result.status}`);
|
|
168
|
+
console.log(` Winner: ${result.winner}`);
|
|
169
|
+
console.log(` Score: ${result.homeScore}-${result.awayScore}`);
|
|
170
|
+
|
|
171
|
+
console.log(`\n🔗 Resolving on-chain...`);
|
|
172
|
+
const oracle = new AutomaticGameOracle(config);
|
|
173
|
+
|
|
174
|
+
// This will resolve on-chain, update Firebase, and send Telegram notification
|
|
175
|
+
await oracle.resolveGame(game, result);
|
|
176
|
+
|
|
177
|
+
console.log(`\n✅ Game resolved successfully!`);
|
|
178
|
+
|
|
179
|
+
if (game.telegramChatId) {
|
|
180
|
+
console.log(` 📱 Telegram notification sent to chat ${game.telegramChatId}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error(`\n❌ Error resolving game:`, error.message);
|
|
185
|
+
|
|
186
|
+
if (error.message?.includes('AlreadyResolved')) {
|
|
187
|
+
console.log(` ℹ️ Game was already resolved`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Resolve all pending games
|
|
196
|
+
*/
|
|
197
|
+
async function resolveAllPendingGames(options) {
|
|
198
|
+
try {
|
|
199
|
+
console.log(`\n🔍 Fetching all pending games...`);
|
|
200
|
+
const games = await fetchPendingGames();
|
|
201
|
+
|
|
202
|
+
if (games.length === 0) {
|
|
203
|
+
console.log(` No pending games found`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log(` Found ${games.length} pending game(s)\n`);
|
|
208
|
+
|
|
209
|
+
for (const game of games) {
|
|
210
|
+
try {
|
|
211
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
212
|
+
await resolveGame(game.gameId, options);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(` Skipping game ${game.gameId}: ${error.message}`);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
220
|
+
console.log(`\n✅ Finished processing all games`);
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error(`\n❌ Error:`, error.message);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Main execution
|
|
230
|
+
*/
|
|
231
|
+
async function main() {
|
|
232
|
+
try {
|
|
233
|
+
// Show usage if no arguments
|
|
234
|
+
if (args.length === 0) {
|
|
235
|
+
console.log('Usage:');
|
|
236
|
+
console.log(' node test-resolve-game.js <gameId> # Resolve specific game');
|
|
237
|
+
console.log(' node test-resolve-game.js --all # Resolve all pending games');
|
|
238
|
+
console.log(' node test-resolve-game.js <gameId> --winner=home # Force home team win');
|
|
239
|
+
console.log(' node test-resolve-game.js <gameId> --winner=away # Force away team win');
|
|
240
|
+
console.log(' node test-resolve-game.js <gameId> --score=3:2 # Force specific score');
|
|
241
|
+
console.log('');
|
|
242
|
+
console.log('Examples:');
|
|
243
|
+
console.log(' node test-resolve-game.js sport-1234567890-abc123');
|
|
244
|
+
console.log(' node test-resolve-game.js sport-1234567890-abc123 --winner=home');
|
|
245
|
+
console.log(' node test-resolve-game.js sport-1234567890-abc123 --winner=away --score=5:2');
|
|
246
|
+
console.log(' node test-resolve-game.js --all');
|
|
247
|
+
console.log('');
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (options.resolveAll) {
|
|
252
|
+
await resolveAllPendingGames(options);
|
|
253
|
+
} else {
|
|
254
|
+
if (!gameIdArg || gameIdArg.startsWith('--')) {
|
|
255
|
+
throw new Error('Please provide a game ID');
|
|
256
|
+
}
|
|
257
|
+
await resolveGame(gameIdArg, options);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log('\n✨ Done!\n');
|
|
261
|
+
process.exit(0);
|
|
262
|
+
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error('\n❌ Fatal error:', error.message);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Run main function
|
|
270
|
+
main();
|
|
271
|
+
|