이 코드는 인프런 강의 자료를 통해서 작성을 하였습니다.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.24;
contract CoinToFlip {
uint constant MAX_CASE = 2;
uint constant MIN_BET = 0.01 ether;
uint constant MAX_BET = 10 ether;
uint constant HOUSE_FEE_PERCENT = 5;
uint constant HOUSE_MIN_FEE = 0.005 ether;
address public owner;
uint public lockedInBets;
struct Bet {
uint amount;
uint8 numOfBetBit;
uint placeBlockNumber;
uint8 mask;
address gambler;
}
mapping (address => Bet) bets;
event Reveal(uint reveal);
event Payment(address indexed beneficiary, uint amount);
event FailedPayment(address indexed beneficiary, uint amount);
constructor () public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner, "오너만 실행 가능합니다.");
_;
}
// 이더를 인출하는 함수
function withdrawFunds(address beneficiary, uint withdrawAmount) external onlyOwner {
require(withdrawAmount + lockedInBets <= address(this).balance, "금액보다 많습니다.");
sendFunds(beneficiary, withdrawAmount);
}
// 금액 이동이 발생할떄 발생하는 함수
function sendFunds(address beneficiary, uint amount) private {
if(beneficiary.send(amount)){
emit Payment(beneficiary, amount);
} else{
emit FailedPayment(beneficiary, amount);
}
}
// 현재 진행중이 게임이 없을경우에만 가능
function kill() external onlyOwner {
require(lockedInBets == 0, "맡겨둔 금액이 있습니다.");
selfdestruct(owner);
}
function () public payable{}
// 금액을 배팅하는 함수
function placeBet(uint8 betMask) external payable{
uint amount = msg.value;
require(amount >= MIN_BET && amount <= MAX_BET, "정해진 범위에서만 배팅 가능합니다.");
require(betMask > 0 && betMask < 256, "배팅 가능한 금액이 정해져 있습니다.");
Bet storage bet = bets[msg.sender];
// null인지 체크하는 부분
require (bet.gambler == address(0),"계좌 오류");
uint8 numOfBetBit = countBits(betMask);
bet.amount = amount;
bet.numOfBetBit = numOfBetBit;
bet.placeBlockNumber = block.number;
bet.mask = betMask;
bet.gambler = msg.sender;
uint possibleWinningAmount = getWinningAmount(amount, numOfBetBit);
lockedInBets += possibleWinningAmount;
require(lockedInBets < address(this).balance, "배팅된 금액보다 큽니다.");
}
function getWinningAmount(uint amount, uint8 numOfBetBit) private pure returns (uint winningAmount) {
require (0 < numOfBetBit && numOfBetBit <MAX_CASE);
uint houseFee = amount * HOUSE_FEE_PERCENT / 100;
if(houseFee < HOUSE_MIN_FEE){
houseFee = HOUSE_MIN_FEE;
}
uint reward = amount / (MAX_CASE + (numOfBetBit - 1));
winningAmount = (amount - houseFee) + reward;
}
function revealResult(uint8 seed) external {
Bet storage bet = bets[msg.sender];
uint amount = bet.amount;
uint8 numOfBetBit = bet.numOfBetBit;
uint placeBlockNumber = bet.placeBlockNumber;
address gambler = bet.gambler;
require(amount>0);
require(block.number > placeBlockNumber);
//난수ㅡㄹ 생성
bytes32 random = keccak256(abi.encodePacked(blockhash(block.number-seed), blockhash(placeBlockNumber)));
uint reveal = uint(random) % MAX_CASE;
uint winningAmount = 0;
uint possibleWinningAmount = 0;
possibleWinningAmount = getWinningAmount(amount, numOfBetBit);
if((2**reveal) & bet.mask != 0){
winningAmount = possibleWinningAmount;
}
emit Reveal(2**reveal);
if(winningAmount>0){
sendFunds(gambler, winningAmount);
}
lockedInBets -= possibleWinningAmount;
clearBet(msg.sender);
}
function clearBet(address player) private{
Bet storage bet = bets[player];
if(bet.amount >0 ){
return;
}
bet.amount = 0;
bet.numOfBetBit = 0;
bet.placeBlockNumber = 0;
bet.mask = 0;
bet.gambler = address(0);
}
function refundbet() external {
require(block.number > bet.placeBlockNumber);
Bet storage bet = bets[msg.sender];
uint amount = bet.amount;
require (amount > 0);
uint8 numOfBetBit = bet.numOfBetBit;
sendFunds(bet.gambler, amount);
uint possibleWinningAmount;
possibleWinningAmount = getWinningAmount(amount, numOfBetBit);
lockedInBets-= possibleWinningAmount;
clearBet(msg.sender);
}
function checkHouseFund() public view onlyOwner returns(uint){
return address(this).balance;
}
function countBits(uint8 _num) internal pure returns (uint8) {
uint8 count;
while(_num >0){
count+= _num & 1;
_num >>= 1;
}
return count;
}
}