Разработка системы джекпотов на блокчейне
Jackpot система в крипто-казино — механика, где часть каждой ставки идёт в накапливаемый приз, который выигрывает случайный игрок. Прогрессивный джекпот создаёт excitement и FOMO — пользователи продолжают играть потому что «в любой момент может сорвать джекпот».
Типы джекпотов
Progressive jackpot: фонд растёт с каждой ставкой (обычно 1-3% от каждой ставки). Нет фиксированного потолка. Редко срабатывает но может вырасти очень большим.
Fixed jackpot: фиксированный приз, который срабатывает при определённом условии (выпадение конкретной комбинации символов в Slots, specific multiplier в Crash).
Must-hit-by jackpot: гарантированно сорвётся до достижения определённой суммы. Снижает дисперсию для оператора.
Tiered jackpot: несколько уровней (Mini, Minor, Major, Grand/Mega). Разные вероятности и размеры. Игроки с большими ставками получают доступ к более высоким тирам.
Smart contract реализация
contract JackpotSystem {
struct Jackpot {
uint256 currentAmount;
uint256 seedAmount; // минимум в джекпоте после срабатывания
uint256 contributionRate; // % от ставок (basis points)
uint256 winProbabilityBase; // базовая вероятность (basis points per unit wagered)
uint256 minTriggerAmount; // минимум для срабатывания
uint256 maxTriggerAmount; // для must-hit-by
bool isActive;
}
Jackpot[4] public jackpots; // MINI, MINOR, MAJOR, GRAND
// Тировые требования (минимальная ставка для участия)
uint256[4] public tierMinBet = [0.001 ether, 0.01 ether, 0.1 ether, 1 ether];
event JackpotWon(uint256 indexed tierId, address winner, uint256 amount);
event JackpotContribution(uint256 indexed tierId, uint256 amount, uint256 newTotal);
// Взнос при каждой ставке
function contributeToJackpots(uint256 betAmount) external onlyGameContract {
for (uint8 tier = 0; tier < 4; tier++) {
if (betAmount >= tierMinBet[tier] && jackpots[tier].isActive) {
uint256 contribution = (betAmount * jackpots[tier].contributionRate) / 10000;
jackpots[tier].currentAmount += contribution;
emit JackpotContribution(tier, contribution, jackpots[tier].currentAmount);
}
}
}
// Проверка jackpot win (вызывается после каждой ставки)
function checkJackpot(
address player,
uint256 betAmount,
uint256 random
) external onlyGameContract returns (uint256 wonTier, uint256 wonAmount) {
for (uint8 tier = 3; tier >= 0; tier--) { // проверяем от GRAND к MINI
Jackpot storage jp = jackpots[tier];
if (!jp.isActive || betAmount < tierMinBet[tier]) continue;
if (jp.currentAmount < jp.minTriggerAmount) continue;
// Вероятность пропорциональна ставке
uint256 probability = (jp.winProbabilityBase * betAmount) / 1 ether;
// Must-hit-by: увеличиваем вероятность при приближении к максимуму
if (jp.maxTriggerAmount > 0 && jp.currentAmount >= jp.maxTriggerAmount * 9 / 10) {
probability = probability * 10; // x10 вероятность в последних 10%
}
uint256 roll = random % 1_000_000;
if (roll < probability) {
wonTier = tier;
wonAmount = jp.currentAmount;
// Сброс джекпота до seed amount
jp.currentAmount = jp.seedAmount;
// Выплата
payable(player).transfer(wonAmount);
emit JackpotWon(tier, player, wonAmount);
break;
}
if (tier == 0) break; // uint8 не уйдёт в negative
}
}
}
Интеграция с Chainlink VRF
Для jackpot критически важен честный random — это самые крупные выплаты:
// Jackpot-specific VRF запрос: более высокий REQUEST_CONFIRMATIONS
function requestJackpotRandom(address player, uint256 betAmount)
external onlyGameContract returns (uint256 requestId)
{
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: KEY_HASH,
subId: SUBSCRIPTION_ID,
requestConfirmations: 5, // дополнительные confirmations для крупных выплат
callbackGasLimit: 150_000,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
jackpotRequests[requestId] = JackpotRequest({ player: player, betAmount: betAmount });
}
Jackpot display — психология накопления
Визуализация растущего счётчика — обязательна:
// Real-time обновление через WebSocket
function JackpotDisplay({ tierId }: { tierId: number }) {
const [amount, setAmount] = useState(0);
const animatedAmount = useAnimatedCounter(amount, 1500); // 1.5s animation
useEffect(() => {
const ws = new WebSocket(WS_URL);
ws.on("jackpot_update", (data) => {
if (data.tierId === tierId) {
setAmount(data.newAmount);
}
});
return () => ws.close();
}, [tierId]);
return (
<div className={`jackpot-display jackpot-tier-${tierId}`}>
<span className="jackpot-label">{TIER_NAMES[tierId]}</span>
<span className="jackpot-amount">
${formatCurrency(animatedAmount)}
</span>
</div>
);
}
Анимированный счётчик, постоянно растущий — мощный психологический триггер. Звуковые эффекты при крупных взносах («тик» каждые $100) усиливают эффект.
История и транзакции
Публичная история побед критична для доверия:
CREATE TABLE jackpot_wins (
id SERIAL PRIMARY KEY,
tier VARCHAR(16) NOT NULL,
winner_address VARCHAR(42) NOT NULL,
amount NUMERIC(36, 18) NOT NULL,
tx_hash VARCHAR(66) NOT NULL,
game_type VARCHAR(64),
won_at TIMESTAMPTZ DEFAULT NOW()
);
Разработка системы джекпотов: 4 уровня + интеграция с игровыми контрактами + Chainlink VRF + real-time display — 3-5 недель.







