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,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Authority Force Lock Script
|
|
4
|
+
* Uses program authority to force-lock games so oracle can resolve them
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* SOLANA_RPC_URL=<mainnet-rpc> node scripts/authority-force-lock.js <gameId>
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* SOLANA_RPC_URL="https://..." node scripts/authority-force-lock.js sport-1769102132440-xmirk1omx
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
const {
|
|
15
|
+
Connection,
|
|
16
|
+
PublicKey,
|
|
17
|
+
Keypair,
|
|
18
|
+
Transaction,
|
|
19
|
+
TransactionInstruction,
|
|
20
|
+
} = require('@solana/web3.js');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const crypto = require('crypto');
|
|
24
|
+
|
|
25
|
+
// Program ID (mainnet)
|
|
26
|
+
const PROGRAM_ID = new PublicKey('85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
|
|
27
|
+
|
|
28
|
+
// authority_force_lock discriminator
|
|
29
|
+
// sha256("global:authority_force_lock")[0..8]
|
|
30
|
+
const AUTHORITY_FORCE_LOCK_DISCRIMINATOR = Buffer.from([
|
|
31
|
+
186, 206, 107, 11, 185, 154, 217, 166
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
// RPC endpoint - use mainnet
|
|
35
|
+
const RPC_URL = process.env.SOLANA_RPC_URL || 'https://solana-mainnet.g.alchemy.com/v2/M7pyy3QL4xYOndpcYukNhf-yq2IG6eyl';
|
|
36
|
+
|
|
37
|
+
console.log('🔐 Authority Force Lock Tool');
|
|
38
|
+
console.log('============================');
|
|
39
|
+
console.log(`RPC: ${RPC_URL.slice(0, 50)}...`);
|
|
40
|
+
console.log(`Program: ${PROGRAM_ID.toString()}`);
|
|
41
|
+
console.log('');
|
|
42
|
+
|
|
43
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load authority wallet (program upgrade authority)
|
|
47
|
+
*/
|
|
48
|
+
function loadAuthorityWallet() {
|
|
49
|
+
const walletPath = process.env.AUTHORITY_WALLET_PATH ||
|
|
50
|
+
path.join(process.env.HOME, '.config/solana/id.json');
|
|
51
|
+
|
|
52
|
+
if (!fs.existsSync(walletPath)) {
|
|
53
|
+
console.error('❌ Authority wallet not found at:', walletPath);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log('🔑 Loading authority wallet from:', walletPath);
|
|
58
|
+
const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf-8'));
|
|
59
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get game PDA and u64 ID from string game ID
|
|
64
|
+
*/
|
|
65
|
+
function getGamePDA(gameIdString) {
|
|
66
|
+
const hash = crypto.createHash('sha256').update(gameIdString).digest();
|
|
67
|
+
const gameIdNum = hash.readBigUInt64LE(0);
|
|
68
|
+
const gameIdBuf = Buffer.alloc(8);
|
|
69
|
+
gameIdBuf.writeBigUInt64LE(gameIdNum);
|
|
70
|
+
|
|
71
|
+
const [gamePDA] = PublicKey.findProgramAddressSync(
|
|
72
|
+
[Buffer.from('game'), gameIdBuf],
|
|
73
|
+
PROGRAM_ID
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return { gamePDA, gameIdNum, gameIdBuf };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build authority_force_lock instruction
|
|
81
|
+
*/
|
|
82
|
+
function buildAuthorityForceLockInstruction(gamePDA, gameIdBuf, authorityWallet) {
|
|
83
|
+
const data = Buffer.concat([
|
|
84
|
+
AUTHORITY_FORCE_LOCK_DISCRIMINATOR,
|
|
85
|
+
gameIdBuf,
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
return new TransactionInstruction({
|
|
89
|
+
keys: [
|
|
90
|
+
{ pubkey: gamePDA, isSigner: false, isWritable: true },
|
|
91
|
+
{ pubkey: authorityWallet.publicKey, isSigner: true, isWritable: true },
|
|
92
|
+
],
|
|
93
|
+
programId: PROGRAM_ID,
|
|
94
|
+
data,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Poll for transaction confirmation
|
|
100
|
+
*/
|
|
101
|
+
async function confirmTransaction(signature, timeout = 60000) {
|
|
102
|
+
const start = Date.now();
|
|
103
|
+
|
|
104
|
+
while (Date.now() - start < timeout) {
|
|
105
|
+
const statuses = await connection.getSignatureStatuses([signature]);
|
|
106
|
+
const status = statuses?.value?.[0];
|
|
107
|
+
|
|
108
|
+
if (status?.err) {
|
|
109
|
+
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
|
|
113
|
+
return status;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new Error('Transaction confirmation timeout');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function forceLockGame(gameIdString) {
|
|
123
|
+
console.log(`📍 Game ID: ${gameIdString}`);
|
|
124
|
+
console.log('');
|
|
125
|
+
|
|
126
|
+
// Load authority wallet
|
|
127
|
+
const authorityWallet = loadAuthorityWallet();
|
|
128
|
+
console.log(` Authority: ${authorityWallet.publicKey.toString()}`);
|
|
129
|
+
|
|
130
|
+
// Get game PDA
|
|
131
|
+
const { gamePDA, gameIdNum, gameIdBuf } = getGamePDA(gameIdString);
|
|
132
|
+
console.log(` Game PDA: ${gamePDA.toString()}`);
|
|
133
|
+
console.log(` Game ID (u64): ${gameIdNum}`);
|
|
134
|
+
|
|
135
|
+
// Check if game exists
|
|
136
|
+
const gameAccount = await connection.getAccountInfo(gamePDA);
|
|
137
|
+
if (!gameAccount) {
|
|
138
|
+
console.error('❌ Game account not found on-chain!');
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
console.log(` Game balance: ${gameAccount.lamports / 1e9} SOL`);
|
|
142
|
+
console.log('');
|
|
143
|
+
|
|
144
|
+
// Build instruction
|
|
145
|
+
const instruction = buildAuthorityForceLockInstruction(gamePDA, gameIdBuf, authorityWallet);
|
|
146
|
+
|
|
147
|
+
// Build transaction
|
|
148
|
+
const latestBlockhash = await connection.getLatestBlockhash('confirmed');
|
|
149
|
+
const transaction = new Transaction();
|
|
150
|
+
transaction.recentBlockhash = latestBlockhash.blockhash;
|
|
151
|
+
transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
152
|
+
transaction.feePayer = authorityWallet.publicKey;
|
|
153
|
+
transaction.add(instruction);
|
|
154
|
+
|
|
155
|
+
// Sign and send
|
|
156
|
+
transaction.sign(authorityWallet);
|
|
157
|
+
|
|
158
|
+
console.log('📡 Sending transaction...');
|
|
159
|
+
try {
|
|
160
|
+
const signature = await connection.sendRawTransaction(transaction.serialize(), {
|
|
161
|
+
skipPreflight: false,
|
|
162
|
+
preflightCommitment: 'confirmed',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
console.log(` Signature: ${signature}`);
|
|
166
|
+
console.log(' Waiting for confirmation...');
|
|
167
|
+
|
|
168
|
+
await confirmTransaction(signature);
|
|
169
|
+
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('✅ Game force-locked successfully!');
|
|
172
|
+
console.log(` Explorer: https://explorer.solana.com/tx/${signature}`);
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log('🔄 The oracle should now be able to resolve this game.');
|
|
175
|
+
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('');
|
|
178
|
+
console.error('❌ Transaction failed:', error.message);
|
|
179
|
+
if (error.logs) {
|
|
180
|
+
console.log('📋 Logs:');
|
|
181
|
+
error.logs.forEach(log => console.log(' ', log));
|
|
182
|
+
}
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Main
|
|
188
|
+
const gameId = process.argv[2];
|
|
189
|
+
if (!gameId) {
|
|
190
|
+
console.log('Usage: node scripts/authority-force-lock.js <gameId>');
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log('Example:');
|
|
193
|
+
console.log(' SOLANA_RPC_URL="https://..." node scripts/authority-force-lock.js sport-1769102132440-xmirk1omx');
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
forceLockGame(gameId);
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Authority Emergency Resolve Script
|
|
4
|
+
* Uses program authority to resolve postponed/stuck games
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node authority-resolve-game.js <gameId> [winner]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* node authority-resolve-game.js sport-1769366095299-m1h01fvt9 # Refund all (no winner)
|
|
11
|
+
* node authority-resolve-game.js sport-1769366095299-m1h01fvt9 home # Home wins
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
require('dotenv').config();
|
|
15
|
+
const {
|
|
16
|
+
Connection,
|
|
17
|
+
PublicKey,
|
|
18
|
+
Keypair,
|
|
19
|
+
Transaction,
|
|
20
|
+
TransactionInstruction,
|
|
21
|
+
LAMPORTS_PER_SOL
|
|
22
|
+
} = require('@solana/web3.js');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const crypto = require('crypto');
|
|
26
|
+
|
|
27
|
+
// Program ID (mainnet)
|
|
28
|
+
const PROGRAM_ID = new PublicKey('85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
|
|
29
|
+
|
|
30
|
+
// Operator wallet (receives fees)
|
|
31
|
+
const OPERATOR_WALLET = new PublicKey('BVZXwZpfgyzTBdRFHohkHZppPHnAyqyctRsKy3vWfQib');
|
|
32
|
+
|
|
33
|
+
// authority_force_refund discriminator (no lock check!)
|
|
34
|
+
const AUTHORITY_FORCE_REFUND_DISCRIMINATOR = Buffer.from([49, 187, 216, 119, 254, 50, 118, 225]);
|
|
35
|
+
|
|
36
|
+
// authority_emergency_resolve discriminator (requires lock)
|
|
37
|
+
const AUTHORITY_EMERGENCY_RESOLVE_DISCRIMINATOR = Buffer.from([31, 157, 128, 122, 207, 17, 218, 155]);
|
|
38
|
+
|
|
39
|
+
// RPC endpoint - use mainnet
|
|
40
|
+
const RPC_URL = process.env.SOLANA_RPC_URL || 'https://solana-mainnet.g.alchemy.com/v2/M7pyy3QL4xYOndpcYukNhf-yq2IG6eyl';
|
|
41
|
+
|
|
42
|
+
console.log('🔧 Authority Emergency Resolve Tool');
|
|
43
|
+
console.log('====================================');
|
|
44
|
+
console.log(`RPC: ${RPC_URL.slice(0, 50)}...`);
|
|
45
|
+
console.log(`Program: ${PROGRAM_ID.toString()}`);
|
|
46
|
+
console.log('');
|
|
47
|
+
|
|
48
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load authority wallet (program upgrade authority)
|
|
52
|
+
*/
|
|
53
|
+
function loadAuthorityWallet() {
|
|
54
|
+
const walletPath = process.env.AUTHORITY_WALLET_PATH ||
|
|
55
|
+
path.join(process.env.HOME, '.config/solana/id.json');
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(walletPath)) {
|
|
58
|
+
console.error('❌ Authority wallet not found at:', walletPath);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('🔑 Loading authority wallet from:', walletPath);
|
|
63
|
+
const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf-8'));
|
|
64
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get game PDA and u64 ID from string game ID
|
|
69
|
+
*/
|
|
70
|
+
function getGamePDA(gameIdString) {
|
|
71
|
+
const hash = crypto.createHash('sha256').update(gameIdString).digest();
|
|
72
|
+
const gameIdNum = hash.readBigUInt64LE(0);
|
|
73
|
+
const gameIdBuf = Buffer.alloc(8);
|
|
74
|
+
gameIdBuf.writeBigUInt64LE(gameIdNum);
|
|
75
|
+
|
|
76
|
+
const [gamePDA] = PublicKey.findProgramAddressSync(
|
|
77
|
+
[Buffer.from('game'), gameIdBuf],
|
|
78
|
+
PROGRAM_ID
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return { gamePDA, gameIdNum, gameIdBuf };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Build authority_force_refund instruction (no lock check!)
|
|
86
|
+
*/
|
|
87
|
+
function buildAuthorityForceRefundInstruction(gamePDA, gameIdBuf, authorityWallet) {
|
|
88
|
+
const data = Buffer.concat([
|
|
89
|
+
AUTHORITY_FORCE_REFUND_DISCRIMINATOR,
|
|
90
|
+
gameIdBuf,
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
return new TransactionInstruction({
|
|
94
|
+
keys: [
|
|
95
|
+
{ pubkey: gamePDA, isSigner: false, isWritable: true },
|
|
96
|
+
{ pubkey: authorityWallet.publicKey, isSigner: true, isWritable: true },
|
|
97
|
+
],
|
|
98
|
+
programId: PROGRAM_ID,
|
|
99
|
+
data,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build authority_emergency_resolve instruction (requires lock)
|
|
105
|
+
*/
|
|
106
|
+
function buildAuthorityResolveInstruction(gamePDA, gameIdBuf, authorityWallet, winningTeam) {
|
|
107
|
+
let winningTeamBuf;
|
|
108
|
+
if (winningTeam === 'home') {
|
|
109
|
+
winningTeamBuf = Buffer.from([1, 0]); // Some(Home)
|
|
110
|
+
} else if (winningTeam === 'away') {
|
|
111
|
+
winningTeamBuf = Buffer.from([1, 1]); // Some(Away)
|
|
112
|
+
} else if (winningTeam === 'draw') {
|
|
113
|
+
winningTeamBuf = Buffer.from([1, 2]); // Some(Draw)
|
|
114
|
+
} else {
|
|
115
|
+
winningTeamBuf = Buffer.from([0]); // None - refund all
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const data = Buffer.concat([
|
|
119
|
+
AUTHORITY_EMERGENCY_RESOLVE_DISCRIMINATOR,
|
|
120
|
+
gameIdBuf,
|
|
121
|
+
winningTeamBuf,
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
return new TransactionInstruction({
|
|
125
|
+
keys: [
|
|
126
|
+
{ pubkey: gamePDA, isSigner: false, isWritable: true },
|
|
127
|
+
{ pubkey: authorityWallet.publicKey, isSigner: true, isWritable: true },
|
|
128
|
+
],
|
|
129
|
+
programId: PROGRAM_ID,
|
|
130
|
+
data,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Poll for transaction confirmation
|
|
136
|
+
*/
|
|
137
|
+
async function confirmTransaction(signature, timeout = 60000) {
|
|
138
|
+
const start = Date.now();
|
|
139
|
+
|
|
140
|
+
while (Date.now() - start < timeout) {
|
|
141
|
+
const statuses = await connection.getSignatureStatuses([signature]);
|
|
142
|
+
const status = statuses?.value?.[0];
|
|
143
|
+
|
|
144
|
+
if (status?.err) {
|
|
145
|
+
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
|
|
149
|
+
return status;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
throw new Error('Transaction confirmation timeout');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Main execution
|
|
160
|
+
*/
|
|
161
|
+
async function main() {
|
|
162
|
+
const args = process.argv.slice(2);
|
|
163
|
+
|
|
164
|
+
if (args.length < 1) {
|
|
165
|
+
console.log('Usage: node authority-resolve-game.js <gameId> [winner]');
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log('Arguments:');
|
|
168
|
+
console.log(' gameId - The game ID string (e.g., sport-1769366095299-m1h01fvt9)');
|
|
169
|
+
console.log(' winner - Optional: home, away, draw (default: refund all)');
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('Examples:');
|
|
172
|
+
console.log(' node authority-resolve-game.js sport-1769366095299-m1h01fvt9');
|
|
173
|
+
console.log(' node authority-resolve-game.js sport-1769366095299-m1h01fvt9 home');
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const gameIdString = args[0];
|
|
178
|
+
const winningTeam = args[1] || null;
|
|
179
|
+
|
|
180
|
+
console.log(`📍 Game ID: ${gameIdString}`);
|
|
181
|
+
console.log(`🏆 Winner: ${winningTeam || 'None (refund all)'}`);
|
|
182
|
+
console.log('');
|
|
183
|
+
|
|
184
|
+
// Load authority wallet
|
|
185
|
+
const authorityWallet = loadAuthorityWallet();
|
|
186
|
+
console.log(` Authority: ${authorityWallet.publicKey.toString()}`);
|
|
187
|
+
|
|
188
|
+
// Verify it matches program authority
|
|
189
|
+
const expectedAuthority = '57voP1Y8U4ztX2YAcHveK3JFvVRn1n6T6iUnHdAW6xr9';
|
|
190
|
+
if (authorityWallet.publicKey.toString() !== expectedAuthority) {
|
|
191
|
+
console.error(`❌ Wallet doesn't match program authority!`);
|
|
192
|
+
console.error(` Expected: ${expectedAuthority}`);
|
|
193
|
+
console.error(` Got: ${authorityWallet.publicKey.toString()}`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
console.log(' ✅ Authority verified');
|
|
197
|
+
|
|
198
|
+
// Get game PDA
|
|
199
|
+
const { gamePDA, gameIdNum, gameIdBuf } = getGamePDA(gameIdString);
|
|
200
|
+
console.log(` Game PDA: ${gamePDA.toString()}`);
|
|
201
|
+
console.log(` Game ID (u64): ${gameIdNum.toString()}`);
|
|
202
|
+
|
|
203
|
+
// Check if game exists on-chain
|
|
204
|
+
const gameAccount = await connection.getAccountInfo(gamePDA);
|
|
205
|
+
if (!gameAccount) {
|
|
206
|
+
console.error('❌ Game account not found on-chain!');
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
console.log(` Game balance: ${gameAccount.lamports / LAMPORTS_PER_SOL} SOL`);
|
|
210
|
+
|
|
211
|
+
// Build instruction based on whether we're refunding or declaring winner
|
|
212
|
+
let ix;
|
|
213
|
+
if (!winningTeam) {
|
|
214
|
+
// Use authority_force_refund (no lock check needed!)
|
|
215
|
+
console.log('');
|
|
216
|
+
console.log('📋 Using authority_force_refund (bypasses lock check)');
|
|
217
|
+
ix = buildAuthorityForceRefundInstruction(gamePDA, gameIdBuf, authorityWallet);
|
|
218
|
+
} else {
|
|
219
|
+
// Use authority_emergency_resolve (requires lock)
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log('📋 Using authority_emergency_resolve (requires game to be locked)');
|
|
222
|
+
ix = buildAuthorityResolveInstruction(gamePDA, gameIdBuf, authorityWallet, winningTeam);
|
|
223
|
+
// Add operator wallet to remaining accounts for fee transfer
|
|
224
|
+
ix.keys.push({ pubkey: OPERATOR_WALLET, isSigner: false, isWritable: true });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Build transaction
|
|
228
|
+
const tx = new Transaction().add(ix);
|
|
229
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
230
|
+
tx.recentBlockhash = blockhash;
|
|
231
|
+
tx.feePayer = authorityWallet.publicKey;
|
|
232
|
+
tx.sign(authorityWallet);
|
|
233
|
+
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log('📡 Sending transaction...');
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const signature = await connection.sendRawTransaction(tx.serialize(), {
|
|
239
|
+
skipPreflight: false,
|
|
240
|
+
preflightCommitment: 'confirmed',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
console.log(` Signature: ${signature}`);
|
|
244
|
+
console.log(' ⏳ Confirming...');
|
|
245
|
+
|
|
246
|
+
await confirmTransaction(signature);
|
|
247
|
+
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log('✅ Game resolved successfully!');
|
|
250
|
+
console.log(` Signature: ${signature}`);
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log('💡 Players can now claim their refunds using the Claim button in the app.');
|
|
253
|
+
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('');
|
|
256
|
+
console.error('❌ Transaction failed:', error.message);
|
|
257
|
+
|
|
258
|
+
if (error.logs) {
|
|
259
|
+
console.error('📋 Logs:');
|
|
260
|
+
error.logs.forEach(log => console.error(' ', log));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
main().catch(console.error);
|