// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol"; import "@chainlink/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol"; contract CoinFlipGame is VRFConsumerBase, KeeperCompatibleInterface { uint public constant GAME_DURATION = 5 minutes; uint public lastGameEndTime; address payable[] public players; mapping(address => uint) public bets; mapping(address => bool) public guesses; uint public totalBetAmount; address payable public owner; bytes32 internal keyHash; uint internal fee; // Events event GameStarted(uint gameEndTime); event BetPlaced(address player, uint amount, bool guess); event GameEnded(bool result, uint totalPayout); event Withdrawal(address to, uint amount); // Commission settings uint public commissionRate = 100; // 1% commission uint public commissionBalance; constructor( address _vrfCoordinator, address _linkToken, bytes32 _keyHash, uint _fee ) VRFConsumerBase(_vrfCoordinator, _linkToken) { owner = payable(msg.sender); keyHash = _keyHash; fee = _fee; lastGameEndTime = block.timestamp + GAME_DURATION; emit GameStarted(lastGameEndTime); } function startNewGame() internal { lastGameEndTime = block.timestamp + GAME_DURATION; players = new address payable[](0); totalBetAmount = 0; emit GameStarted(lastGameEndTime); } function placeBet(bool _guess) external payable { require(block.timestamp < lastGameEndTime, "Game has ended"); require(msg.value > 0, "Bet amount must be greater than 0"); uint commission = (msg.value * commissionRate) / 10000; uint betAmount = msg.value - commission; commissionBalance += commission; players.push(payable(msg.sender)); bets[msg.sender] = betAmount; guesses[msg.sender] = _guess; totalBetAmount += betAmount; emit BetPlaced(msg.sender, betAmount, _guess); } function manualResetGame() external { require(msg.sender == owner, "Only the owner can reset the game"); resetGame(); } function withdraw() external { require(msg.sender == owner, "Only the owner can withdraw"); uint amount = commissionBalance; commissionBalance = 0; owner.transfer(amount); emit Withdrawal(owner, amount); } function resetGame() internal { for (uint i = 0; i < players.length; i++) { bets[players[i]] = 0; guesses[players[i]] = false; } players = new address payable[](0); totalBetAmount = 0; startNewGame(); } function checkUpkeep(bytes calldata /* checkData */) external view override returns (bool upkeepNeeded, bytes memory /* performData */) { upkeepNeeded = (block.timestamp >= lastGameEndTime) && (address(this).balance >= fee); } function performUpkeep(bytes calldata /* performData */) external override { require((block.timestamp >= lastGameEndTime) && (address(this).balance >= fee), "Game is still running or not enough LINK"); endGame(); } function endGame() internal { require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet"); requestRandomness(keyHash, fee); } function fulfillRandomness(bytes32 /* requestId */, uint256 randomness) internal override { bool flipResult = randomness % 2 == 0; uint totalPayout = 0; for (uint i = 0; i < players.length; i++) { if (guesses[players[i]] == flipResult) { uint payout = bets[players[i]] * 2; totalPayout += payout; payable(players[i]).transfer(payout); } } emit GameEnded(flipResult, totalPayout); resetGame(); } // Add any other necessary functions and modifiers here ... }
0.4.18