kba / services /strikeEngine.js
ShieldX's picture
Upload 24 files
e14bacb verified
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 };