const axios = require("axios"); const crypto = require("crypto"); const Asset = require("../models/Asset"); const User = require("../models/User"); const Transaction = require("../models/Transaction"); const StrikeHistory = require("../models/StrikeHistory"); const { sendGlobal, sendToUser } = require('../utils/sendPush'); const WIN_PCT = { Silver: [0.30, 0.15, 0.08], Gold: [0.40, 0.20, 0.10], Platinum: [0.50, 0.25, 0.125] }; const circularDiff = (a, b, mod) => { const diff = Math.abs(a - b); return Math.min(diff, mod - diff); }; const secureShuffle = (arr) => { for (let i = arr.length - 1; i > 0; i--) { const j = crypto.randomInt(0, i + 1); [arr[i], arr[j]] = [arr[j], arr[i]]; } }; const sha256 = (payload) => crypto.createHash("sha256").update(JSON.stringify(payload)).digest("hex"); async function runStrike({ triggeredBy = "CRON" } = {}) { if (global.isStrikeRunning) return; global.isStrikeRunning = true; global.lastStrikeStatus = "RUNNING"; global.lastStrikeResult = null; try { // ⛔ prevent double strike per day const startOfDay = new Date(); startOfDay.setUTCHours(0, 0, 0, 0); const already = await StrikeHistory.findOne({ date: { $gte: startOfDay } }); if (already) { global.lastStrikeStatus = "COMPLETED"; return; } // 🔔 START EVENT await sendGlobal( "⚡ Strike Started!", triggeredBy === "ADMIN" ? "Admin manually triggered today's strike." : "The 8 PM Strike is LIVE.", "/" ); // 1️⃣ BTC PRICE const { data } = await axios.get( "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT" ); const price = parseFloat(data.price).toFixed(2); const btcInt = price.split(".")[0]; const btcLast3 = parseInt(btcInt.slice(-3)); const btcLast6 = parseInt(btcInt.slice(-6)); global.liveStrikeData = { price, btcLast3, btcLast6 }; // 2️⃣ POOL const txs = await Transaction.find({ timestamp: { $gte: startOfDay }, status: "Verified" }); const totalPool = txs.reduce((s, t) => s + t.amount, 0); if (!totalPool) { global.lastStrikeStatus = "CANCELLED"; return; } // 3️⃣ ASSETS const assets = await Asset.find({ purchase_date: { $gte: startOfDay }, status: "Held" }).populate("owner_id"); // 4️⃣ LOW PARTICIPATION → CANCEL if (assets.length <= 3) { const prices = { Silver: 500, Gold: 1000, Platinum: 2500 }; for (const a of assets) { await User.findByIdAndUpdate(a.owner_id._id, { $inc: { wallet_balance: prices[a.tier] } }); a.status = "Burned"; await a.save(); } global.lastStrikeStatus = "CANCELLED"; global.lastStrikeResult = { cancelled: true }; await sendGlobal( "🚫 Strike Cancelled", "Low participation. Refunds issued.", "/profile" ); return; } // 5️⃣ RANKING let ranked = assets.map(asset => { const kusLast6 = parseInt(asset.kus_id.slice(-6)); const kusLast3 = kusLast6 % 1000; return { asset, diff3: circularDiff(kusLast3, btcLast3, 1000), diff6: circularDiff(kusLast6, btcLast6, 1_000_000) }; }); ranked.sort((a, b) => { if (a.diff3 !== b.diff3) return a.diff3 - b.diff3; if (a.diff6 !== b.diff6) return a.diff6 - b.diff6; return 0; }); secureShuffle(ranked.slice(0, 3)); // 6️⃣ WINNERS const historyWinners = []; for (let i = 0; i < 3; i++) { const { asset } = ranked[i]; const pct = WIN_PCT[asset.tier][i]; if (!pct) continue; const gross = totalPool * pct; const net = gross * (1 - 0.312); await User.findByIdAndUpdate(asset.owner_id._id, { $inc: { wallet_balance: net } }); await Transaction.create({ user_id: asset.owner_id._id, amount: net, status: "Verified", tier: asset.tier, utr: `STRIKE-${Date.now()}` }); historyWinners.push({ tier: asset.tier, user_id: asset.owner_id._id, amount: net, rank: i + 1, kus_id: asset.kus_id }); await sendToUser( asset.owner_id._id, "🏆 YOU WON!", `Rank #${i + 1} in today's strike.`, "/profile" ); } // 7️⃣ AUDIT const auditPayload = { price, btcLast3, btcLast6, pool: totalPool, participants: assets.map(a => a.kus_id), winners: historyWinners, triggeredBy }; const auditHash = sha256(auditPayload); await StrikeHistory.create({ target_price: price, winning_digits: btcLast3, total_pool: totalPool, audit_hash: auditHash, audit_payload: auditPayload, winners: historyWinners }); // ✅ FINAL GLOBAL RESULT (frontend reads this) global.lastStrikeStatus = "COMPLETED"; global.lastStrikeResult = { cancelled: false, winningDigits: btcLast3, winners: historyWinners }; await sendGlobal( "🏁 Results Announced", "Strike completed successfully.", "/" ); } catch (err) { console.error("❌ Strike failed:", err); global.lastStrikeStatus = "FAILED"; } finally { global.isStrikeRunning = false; global.liveStrikeData = null; console.log("⚡ STRIKE ENDED ⚡"); } } module.exports = { runStrike };