From 8b6b1c3a5bf2ceb50ea6a69c8a13fdbda1de8dab Mon Sep 17 00:00:00 2001 From: Lulox Date: Mon, 8 Jan 2024 21:01:08 -0300 Subject: [PATCH] Deleted TicTacToe testing contracts and fixed withdraw bet amount for ties --- packages/hardhat/contracts/GPTicTacToe.sol | 221 ------------------ packages/hardhat/contracts/Sportsbook.sol | 4 +- packages/hardhat/contracts/TicTacToe.sol | 163 ------------- .../hardhat/deploy/01_deploy_tictactoe.ts | 42 ---- 4 files changed, 2 insertions(+), 428 deletions(-) delete mode 100644 packages/hardhat/contracts/GPTicTacToe.sol delete mode 100755 packages/hardhat/contracts/TicTacToe.sol delete mode 100755 packages/hardhat/deploy/01_deploy_tictactoe.ts diff --git a/packages/hardhat/contracts/GPTicTacToe.sol b/packages/hardhat/contracts/GPTicTacToe.sol deleted file mode 100644 index 9acdd0f..0000000 --- a/packages/hardhat/contracts/GPTicTacToe.sol +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -contract GPTicTacToe { - address public owner; - uint256 public taxRate = 3; // 3% tax - uint256 public gameIdCounter = 1; - - enum GameState { - Open, - InProgress, - Player1Won, - Player2Won, - Draw, - Canceled - } - - struct Game { - address player1; - address player2; - uint256 betAmount; - uint256 startTime; - GameState state; - uint8[9] board; // 0: empty, 1: X, 2: O - } - - mapping(address => uint256) public balances; - mapping(address => bool) public allowedWithdrawal; - - mapping(uint256 => Game) public games; - - event GameCreated(uint256 indexed gameId, address indexed player1, address indexed player2, uint256 betAmount); - event MoveMade(uint256 indexed gameId, address indexed player, uint8 position); - event GameFinished(uint256 indexed gameId, address indexed winner, GameState state); - event PrizeClaimed(uint256 indexed gameId, address indexed winner, uint256 amount); - event GameCanceled(uint256 indexed gameId, address indexed canceler); - - modifier onlyOwner() { - require(msg.sender == owner, "Not the contract owner"); - _; - } - - modifier onlyPlayers(uint256 gameId) { - require(msg.sender == games[gameId].player1 || msg.sender == games[gameId].player2, "Not a player"); - _; - } - - modifier onlyValidMove(uint256 gameId, uint8 position) { - require(games[gameId].board[position] == 0, "Invalid move"); - _; - } - - modifier onlyDuringGame(uint256 gameId) { - require(games[gameId].state == GameState.InProgress, "Game not in progress"); - _; - } - - modifier onlyAfterGame(uint256 gameId) { - require( - games[gameId].state == GameState.Player1Won || games[gameId].state == GameState.Player2Won - || games[gameId].state == GameState.Draw || games[gameId].state == GameState.Canceled, - "Game not finished" - ); - _; - } - - constructor() { - owner = msg.sender; - } - - function createGame(address player2, uint256 betAmount) external payable { - require(msg.value == betAmount, "Incorrect bet amount"); - uint256 gameId = uint256(keccak256(abi.encodePacked(gameIdCounter, block.timestamp, msg.sender, player2))); - - games[gameId] = Game({ - player1: msg.sender, - player2: player2, - betAmount: betAmount, - startTime: block.timestamp, - state: GameState.InProgress, - board: [0, 0, 0, 0, 0, 0, 0, 0, 0] - }); - - gameIdCounter++; - - emit GameCreated(gameId, msg.sender, player2, betAmount); - } - - function makeMove(uint256 gameId, uint8 position) - external - onlyPlayers(gameId) - onlyDuringGame(gameId) - onlyValidMove(gameId, position) - { - require(msg.sender == getCurrentPlayer(gameId), "Not your turn"); - require(position < 9, "Invalid position"); - - uint8 currentPlayerSymbol = getCurrentPlayerSymbol(gameId); - games[gameId].board[position] = currentPlayerSymbol; - - emit MoveMade(gameId, msg.sender, position); - - if (checkWin(gameId, currentPlayerSymbol)) { - finishGame(gameId, msg.sender, GameState.Player1Won); - } else if (checkDraw(gameId)) { - finishGame(gameId, address(0), GameState.Draw); - } else { - toggleTurn(gameId); - } - } - - function claimPrize(uint256 gameId) external onlyAfterGame(gameId) { - address winner = getWinner(gameId); - require(winner == msg.sender, "You are not the winner"); - - uint256 prizeAmount = games[gameId].betAmount * 2; - balances[msg.sender] += prizeAmount; - - emit PrizeClaimed(gameId, msg.sender, prizeAmount); - - resetGame(gameId); - } - - function cancelGame(uint256 gameId) external onlyPlayers(gameId) { - require(msg.sender == games[gameId].player1, "Not player1"); - - games[gameId].state = GameState.Canceled; - allowedWithdrawal[games[gameId].player1] = true; - - emit GameCanceled(gameId, msg.sender); - } - - function withdraw() external { - require(allowedWithdrawal[msg.sender], "Not allowed to withdraw"); - uint256 amount = balances[msg.sender]; - require(amount > 0, "No balance to withdraw"); - - balances[msg.sender] = 0; - allowedWithdrawal[msg.sender] = false; - - (bool success,) = msg.sender.call{value: amount}(""); - require(success, "Withdrawal failed"); - } - - function setTaxRate(uint256 newRate) external onlyOwner { - require(newRate <= 10, "Tax rate can't exceed 10%"); - taxRate = newRate; - } - - function withdrawTax() external onlyOwner { - uint256 taxAmount = address(this).balance * taxRate / 100; - require(taxAmount > 0, "No tax to withdraw"); - - (bool success,) = owner.call{value: taxAmount}(""); - require(success, "Tax withdrawal failed"); - } - - // Internal functions - - function getCurrentPlayer(uint256 gameId) internal view returns (address) { - return (block.timestamp / 2) % 2 == 0 ? games[gameId].player1 : games[gameId].player2; - } - - function getCurrentPlayerSymbol(uint256 gameId) internal view returns (uint8) { - return getCurrentPlayer(gameId) == games[gameId].player1 ? 1 : 2; - } - - function toggleTurn(uint256 gameId) internal { - allowedWithdrawal[games[gameId].player1] = !allowedWithdrawal[games[gameId].player1]; - allowedWithdrawal[games[gameId].player2] = !allowedWithdrawal[games[gameId].player2]; - } - - function checkWin(uint256 gameId, uint8 playerSymbol) internal view returns (bool) { - uint8[3][8] memory winConditions = [ - [0, 1, 2], - [3, 4, 5], - [6, 7, 8], // Rows - [0, 3, 6], - [1, 4, 7], - [2, 5, 8], // Columns - [0, 4, 8], - [2, 4, 6] // Diagonals - ]; - - for (uint8 i = 0; i < 8; i++) { - uint8[3] memory condition = winConditions[i]; - if ( - games[gameId].board[condition[0]] == playerSymbol && games[gameId].board[condition[1]] == playerSymbol - && games[gameId].board[condition[2]] == playerSymbol - ) { - return true; - } - } - - return false; - } - - function checkDraw(uint256 gameId) internal view returns (bool) { - for (uint8 i = 0; i < 9; i++) { - if (games[gameId].board[i] == 0) { - return false; // Game still has empty spots - } - } - - return true; // All spots filled, but no winner - } - - function finishGame(uint256 gameId, address winner, GameState state) internal { - games[gameId].state = state; - emit GameFinished(gameId, winner, state); - } - - function resetGame(uint256 gameId) internal { - delete games[gameId]; - } - - function getWinner(uint256 gameId) internal view returns (address) { - uint8 winnerSymbol = getCurrentPlayerSymbol(gameId) == 1 ? 2 : 1; - return winnerSymbol == 1 ? games[gameId].player1 : games[gameId].player2; - } -} diff --git a/packages/hardhat/contracts/Sportsbook.sol b/packages/hardhat/contracts/Sportsbook.sol index b445eaa..e647755 100755 --- a/packages/hardhat/contracts/Sportsbook.sol +++ b/packages/hardhat/contracts/Sportsbook.sol @@ -151,9 +151,9 @@ contract Sportsbook { require(!matchChallenges[_challengeId].team2Withdrawn, "You have already withdrawn your share."); matchChallenges[_challengeId].team2Withdrawn = true; } - (bool successTie,) = payable(msg.sender).call{value: matchChallenges[_challengeId].bet / 2}(""); + (bool successTie,) = payable(msg.sender).call{value: matchChallenges[_challengeId].bet}(""); require(successTie, "Transfer failed."); - emit PrizeWithdrawn(_challengeId, msg.sender, matchChallenges[_challengeId].bet / 2); + emit PrizeWithdrawn(_challengeId, msg.sender, matchChallenges[_challengeId].bet); return; } diff --git a/packages/hardhat/contracts/TicTacToe.sol b/packages/hardhat/contracts/TicTacToe.sol deleted file mode 100755 index a67b2bd..0000000 --- a/packages/hardhat/contracts/TicTacToe.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -/** - * @title A Tic Tac Toe game - * @author Lulox - * @notice This contract is for creating a bet between two parts on the outcome of a Tic Tac Toe Game - */ -contract TicTacToe { - uint256 public gameIdCounter = 1; - - enum GameState { - PENDING, - PLAYING, - PLAYER1WON, - PLAYER2WON, - TIE, - CANCELED - } - - struct Game { - address player1; - address player2; - GameState state; - uint256 bet; - uint256 lastMoveTime; - bool player1Withdrawn; // Indicates whether player 1 has withdrawn or not - bool player2Withdrawn; // Indicates whether player 2 has withdrawn or not - uint8[9] board; // 0: empty, 1: X, 2: O - } - - mapping(bytes32 => Game) public games; - - event GameCreated(bytes32 indexed gameId, address indexed player1, address indexed player2, uint256 bet); - event GameAccepted(bytes32 indexed gameId, address indexed team1, address indexed team2); - event MoveMade(bytes32 indexed gameId, address indexed player, uint8 position); - event GameFinished(bytes32 indexed gameId, address indexed winner, GameState state); - - modifier onlyPlayers(bytes32 gameId) { - require(msg.sender == games[gameId].player1 || msg.sender == games[gameId].player2, "Not a player"); - _; - } - - modifier onlyDuringGame(bytes32 gameId) { - require(games[gameId].state == GameState.PLAYING, "Game not in progress"); - _; - } - - modifier onlyValidMove(bytes32 gameId, uint8 position) { - require(games[gameId].board[position] == 0, "Invalid move"); - _; - } - - function createGame(address _player2) external payable returns (bytes32 gameId) { - gameId = keccak256(abi.encodePacked(gameIdCounter, block.timestamp, msg.sender, _player2)); - - games[gameId] = Game({ - player1: msg.sender, - player2: _player2, - state: GameState.PENDING, - bet: msg.value, - lastMoveTime: block.timestamp, - player1Withdrawn: false, - player2Withdrawn: false, - board: [0, 0, 0, 0, 0, 0, 0, 0, 0] - }); - - gameIdCounter++; - - emit GameCreated(gameId, msg.sender, _player2, msg.value); - } - - function makeMove(bytes32 _gameId, uint8 position) - external - payable - onlyPlayers(_gameId) - onlyDuringGame(_gameId) - onlyValidMove(_gameId, position) - { - if (games[_gameId].player2 == msg.sender && games[_gameId].state == GameState.PENDING) { - require(msg.value >= games[_gameId].bet, "Haven't sent enough ETH!"); - acceptGame(_gameId); - } - require(msg.sender == getCurrentPlayer(_gameId), "Not your turn"); - require(position < 9, "Invalid position"); - - uint8 currentPlayerSymbol = getCurrentPlayerSymbol(_gameId); - games[_gameId].board[position] = currentPlayerSymbol; - - emit MoveMade(_gameId, msg.sender, position); - - // if (checkWin(_gameId, currentPlayerSymbol)) { - // finishGame(_gameId, msg.sender, GameState.PLAYER1WON); - // } else if (checkDraw(_gameId)) { - // finishGame(_gameId, address(0), GameState.TIE); - // } else { - // toggleTurn(_gameId); - // } - } - - function getBoard(bytes32 _gameId) external view returns (uint8[9] memory) { - return games[_gameId].board; - } - - function acceptGame(bytes32 _gameId) internal { - games[_gameId].state = GameState.PLAYING; - games[_gameId].lastMoveTime = block.timestamp; - - emit GameAccepted(_gameId, games[_gameId].player1, games[_gameId].player2); - } - - function finishGame(bytes32 gameId, address winner, GameState state) internal { - games[gameId].state = state; - emit GameFinished(gameId, winner, state); - } - - function checkWin(bytes32 gameId, uint8 playerSymbol) internal view returns (bool) { - uint8[3][8] memory winConditions = [ - [0, 1, 2], - [3, 4, 5], - [6, 7, 8], // Rows - [0, 3, 6], - [1, 4, 7], - [2, 5, 8], // Columns - [0, 4, 8], - [2, 4, 6] // Diagonals - ]; - - for (uint8 i = 0; i < 8; i++) { - uint8[3] memory condition = winConditions[i]; - if ( - games[gameId].board[condition[0]] == playerSymbol && games[gameId].board[condition[1]] == playerSymbol - && games[gameId].board[condition[2]] == playerSymbol - ) { - return true; - } - } - - return false; - } - - function checkDraw(bytes32 gameId) internal view returns (bool) { - for (uint8 i = 0; i < 9; i++) { - if (games[gameId].board[i] == 0) { - return false; // Game still has empty spots - } - } - - return true; // All spots filled, but no winner - } - - function getCurrentPlayerSymbol(bytes32 gameId) internal view returns (uint8) { - return getCurrentPlayer(gameId) == games[gameId].player1 ? 1 : 2; - } - - // What is this function doing????? - function getCurrentPlayer(bytes32 gameId) internal view returns (address) { - return (block.timestamp / 2) % 2 == 0 ? games[gameId].player1 : games[gameId].player2; - } - - // Maybe this could be removed and the turn could be calculated by checking if the amount of moves is even or odd - function toggleTurn(bytes32 gameId) internal {} -} diff --git a/packages/hardhat/deploy/01_deploy_tictactoe.ts b/packages/hardhat/deploy/01_deploy_tictactoe.ts deleted file mode 100755 index e7b38ec..0000000 --- a/packages/hardhat/deploy/01_deploy_tictactoe.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { DeployFunction } from "hardhat-deploy/types"; - -/** - * Deploys a contract named "YourContract" using the deployer account and - * constructor arguments set to the deployer address - * - * @param hre HardhatRuntimeEnvironment object. - */ -const deployTicTacToe: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - /* - On localhost, the deployer account is the one that comes with Hardhat, which is already funded. - - When deploying to live networks (e.g `yarn deploy --network goerli`), the deployer account - should have sufficient balance to pay for the gas fees for contract creation. - - You can generate a random account with `yarn generate` which will fill DEPLOYER_PRIVATE_KEY - with a random private key in the .env file (then used on hardhat.config.ts) - You can run the `yarn account` command to check your balance in every network. - */ - const { deployer } = await hre.getNamedAccounts(); - const { deploy } = hre.deployments; - - await deploy("TicTacToe", { - from: deployer, - // Contract constructor arguments - args: [], - log: true, - // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by - // automatically mining the contract deployment transaction. There is no effect on live networks. - autoMine: true, - }); - - // Get the deployed contract - // const yourContract = await hre.ethers.getContract("YourContract", deployer); -}; - -export default deployTicTacToe; - -// Tags are useful if you have multiple deploy files and only want to run one of them. -// e.g. yarn deploy --tags YourContract -deployTicTacToe.tags = ["TicTacToe"];