| 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 { |
| |
| const startOfDay = new Date(); |
| startOfDay.setUTCHours(0, 0, 0, 0); |
|
|
| const already = await StrikeHistory.findOne({ |
| date: { $gte: startOfDay } |
| }); |
|
|
| if (already) { |
| global.lastStrikeStatus = "COMPLETED"; |
| return; |
| } |
|
|
| |
| await sendGlobal( |
| "⚡ Strike Started!", |
| triggeredBy === "ADMIN" |
| ? "Admin manually triggered today's strike." |
| : "The 8 PM Strike is LIVE.", |
| "/" |
| ); |
|
|
| |
| 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 |
| }; |
|
|
| |
| 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; |
| } |
|
|
| |
| const assets = await Asset.find({ |
| purchase_date: { $gte: startOfDay }, |
| status: "Held" |
| }).populate("owner_id"); |
|
|
| |
| 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; |
| } |
|
|
| |
| 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)); |
|
|
| |
| 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" |
| ); |
| } |
|
|
| |
| 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 |
| }); |
|
|
| |
| 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 }; |
|
|