Retypeme docs
GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Contract

Test wallets

ValueHash
creator0x93bfAD49c590A4E1f06A67EfbD4c90F94f501a85
ap.test10xb4352Cf87739c3E494fd8fC405510dA02b61f66E
ap.test20x5057376A02B717e8B262C6fad5166c17e194D277
ik.test10x470Eb274064Dc3134470DC0CB85f7c6E87164C18

Testnets

Contract

Deployed Contracts

Version 1.0 - 01.03.2024

NetworkContract Address
Blast0xf813b4e5d34079ebcc59adf39a7782ad989891fe
ScrollNot available
Polygon0xb3c33b58de859a5e06aff62c9d66319c256218da
BNB ChainNot available

Source code for latest contract version

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

interface IGamingContract {
    function deposit() external payable;

    function withdraw(uint256) external;

    function joinGame(uint256 _sessionId) external;

    function getBalance(address _user) external view returns (uint256);

    function isEnoughBalance(address _user) external view returns (bool);

    function getMinDeposit(address _user) external view returns (uint256);

    function endGame(uint256 _sessionId, address _winner) external;
}

contract GamingContract is IGamingContract {
    // declare constant default players count for duel mode
    uint256 public constant DUEL_PLAYERS_COUNT = 2;

    address public owner;
    uint256 public commissionRate;
    uint256 public fixedDepositAmount;
    uint256 public totalCommission;

    enum GameStatus {
        NOT_STARTED,
        STARTED,
        ENDED
    }

    struct GameSession {
        uint256 depositAmount;
        uint256 sessionId;
        GameStatus status;
        address[] players;
        address winner;
    }

    mapping(address => uint256) public inGameUserBalances;
    mapping(uint256 => GameSession) public gameSessions;

    event GameStarted(uint256 indexed sessionId);
    event GameEnded(uint256 indexed sessionId, address winner, uint256 winnings);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    constructor() {
        owner = msg.sender;
        commissionRate = 20;
        fixedDepositAmount = 0.001 ether;
    }

    function deposit() external payable {
        inGameUserBalances[msg.sender] += msg.value;
    }

    function getBalance(address _user) external view returns (uint256) {
        return inGameUserBalances[_user];
    }

    function getMinDeposit(address _user) external view returns (uint256) {
        // if there is no enough balance, show min amount to deposit to be able to play a game
        return fixedDepositAmount > inGameUserBalances[_user] ? fixedDepositAmount - inGameUserBalances[_user] : 0;
    }

    function isEnoughBalance(address _user) external view returns (bool) {
        return inGameUserBalances[_user] >= fixedDepositAmount;
    }

    // 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02d1
    function joinGame(uint256 _sessionId) external {
        // Check if the session already exists by checking a characteristic field, like sessionId
        if (gameSessions[_sessionId].sessionId == 0) {
            // This implies the session does not exist, so initialize it
            gameSessions[_sessionId].sessionId = _sessionId;
            gameSessions[_sessionId].depositAmount = 0; // Assuming it starts at 0 and increments with each deposit
            gameSessions[_sessionId].status = GameStatus.NOT_STARTED;
            gameSessions[_sessionId].players = new address[](0);
            gameSessions[_sessionId].winner = address(0);
        }
        // Proceed with existing checks
        require(
            gameSessions[_sessionId].players.length == 0 || gameSessions[_sessionId].players[0] != msg.sender,
            "Player already in the game"
        );
        require(gameSessions[_sessionId].players.length < DUEL_PLAYERS_COUNT, "Game session is full");
        require(gameSessions[_sessionId].status == GameStatus.NOT_STARTED, "Game session already started");
        require(inGameUserBalances[msg.sender] >= fixedDepositAmount, "Insufficient balance");

        // Lock sender balance
        inGameUserBalances[msg.sender] -= fixedDepositAmount;
        gameSessions[_sessionId].depositAmount += fixedDepositAmount;

        // Add the sender to the players list
        gameSessions[_sessionId].players.push(msg.sender);

        // If the session becomes full, start the game
        if (gameSessions[_sessionId].players.length == DUEL_PLAYERS_COUNT) {
            gameSessions[_sessionId].status = GameStatus.STARTED;
            emit GameStarted(_sessionId);
        }
    }

    function endGame(uint256 _sessionId, address _winner) external onlyOwner {
        GameSession storage session = gameSessions[_sessionId];
        // check if the game is started state
        require(session.status == GameStatus.STARTED, "Game not started or already ended");
        require(session.players.length == DUEL_PLAYERS_COUNT, "Wrong number of players in the game session");
        require(
            _winner == session.players[0] || _winner == session.players[1],
            "This address can't be the winner. It is not in the current game session."
        );
        // Logic to determine the winner and calculate winnings and commission
        // For simplicity, assuming winner is provided and winnings are total deposit amount minus commission
        // ADDED new variable (winnings) to make gas cheaper
        uint256 commission = (session.depositAmount * commissionRate) / 100;
        uint256 winnings = session.depositAmount - commission;
        // update winner balance
        inGameUserBalances[_winner] += winnings;
        totalCommission += commission;
        // update winner
        session.winner = _winner;
        // update session status
        session.status = GameStatus.ENDED;
        emit GameEnded(_sessionId, _winner, winnings);
    }

    // I think NO need to make two withdraw fns, because it will always get amount from FE after reading state. (there will be input
    // with desirable amount and button "Max", it will check max balance and pass it to the withdraw fn)
    function withdraw(uint256 _amount) external {
        address sender = msg.sender;
        require(inGameUserBalances[sender] >= _amount, "Insufficient balance");
        inGameUserBalances[sender] -= _amount;
        payable(sender).transfer(_amount);
    }

    function withdrawFundsFromContract() external onlyOwner {
        // we are withdrawing all the balance. will be deleted in future versions!
        require(address(this).balance > 0, "No funds to withdraw");
        payable(owner).transfer(address(this).balance);
    }

    function withdrawCommissionFromContract() external onlyOwner {
        // withdrawing only total commission
        require(totalCommission > 0, "No commission to withdraw");
        totalCommission = 0; // MODIFY FIRST
        // payable(owner).transfer(totalCommission); // THEN TRANSFER
        payable(owner).transfer(totalCommission); // THEN TRANSFER
    }

    function changeCommissionRate(uint256 _newCommissionRate) external onlyOwner {
        require(_newCommissionRate >= 0 && _newCommissionRate < 100, "Enter valid number");
        commissionRate = _newCommissionRate;
    }

    function changeFixedDepositAmount(uint256 _newFixedDepositAmount) external onlyOwner {
        require(_newFixedDepositAmount > 0, "Enter valid number");
        fixedDepositAmount = _newFixedDepositAmount;
    }
}