peterduhon / skia-poker-morph

Trustless Tricksters: A decentralized poker game built on Morph zkEVM. Featuring secure card shuffling, Chainlink VRF for fairness, Galadriel AI agents, XMTP chat, and Web3Auth login. Experience the future of poker on the blockchain. 🃏🔐🚀
3 stars 0 forks source link

[SP-21] Testing Skia Poker Smart Contracts #16

Open peterduhon opened 2 months ago

peterduhon commented 2 months ago

[SP-21] Testing Skia Poker Smart Contracts

Summary:
Perform comprehensive testing on the Skia Poker smart contracts to ensure functionality, interactions, and security across all six contracts. The testing plan includes unit, integration, state transition, edge case, security, and gas optimization testing.

Description:
This task focuses on setting up a testing environment for the Skia Poker smart contracts, writing test scripts for critical functions, and performing both unit and integration tests. Additionally, edge case testing, gas optimization, and security reviews will be conducted to ensure robust contract functionality.

The goal is to ensure that all contracts perform as expected, with no security vulnerabilities, and are optimized for gas efficiency.

Acceptance Criteria:

Tasks:

  1. Unit Testing:

    • Test each contract function individually.
    • Ensure each function behaves correctly with valid and invalid inputs.
  2. Integration Testing:

    • Test interactions between the six smart contracts.
    • Verify that they work together without errors.
  3. State Transition Testing:

    • Test transitions between game states (e.g., from waiting to active, active to finished).
    • Verify correct behavior in each state.
  4. Edge Case Testing:

    • Test with minimum and maximum player counts.
    • Test with minimum and maximum bet amounts.
    • Test scenarios like all players folding.
  5. Security Testing:

    • Check for reentrancy vulnerabilities.
    • Ensure only authorized addresses can call sensitive functions.
    • Verify access control is properly implemented.
  6. Gas Optimization:

    • Run gas usage reports on key functions.
    • Identify and note any gas inefficiencies for future improvements.

Timeline:

Dependencies:

Priority: High
Assignee: James/@coolman Due Date: Sept 7, 2024, 11:00 AM UTC

Risks and Mitigations:

Testing Guide

For detailed instructions on running and interpreting our test suites, please refer to the Skia Testing Guide in the resources folder.

peterduhon commented 2 months ago

Unit Testing Framework for Skia Poker Contracts

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker Unit Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");

  beforeEach(async function() {
    [owner, player1, player2, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      0, // roomId
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero, // VRF Coordinator (mock for testing)
      ethers.constants.AddressZero, // LINK token (mock for testing)
      ethers.utils.formatBytes32String(""), // keyHash (mock for testing)
      ethers.utils.parseEther("0.1") // fee (mock for testing)
    );
  });

  describe("Game Initialization", function() {
    it("should initialize game state correctly", async function() {
      expect(await bettingAndPotManagement.gameState()).to.equal(0); // WaitingForPlayers
    });

    it("should set minimum bet correctly", async function() {
      expect(await bettingAndPotManagement.minimumBet()).to.equal(minimumBet);
    });
  });

  describe("Player Management", function() {
    it("should allow players to join the game", async function() {
      await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
      const player = await bettingAndPotManagement.players(player1.address);
      expect(player.addr).to.equal(player1.address);
      expect(player.balance).to.equal(minimumBet);
    });

    it("should not allow players to join with insufficient funds", async function() {
      await expect(
        bettingAndPotManagement.connect(player1).joinGame({ value: ethers.utils.parseEther("0.05") })
      ).to.be.revertedWith("Insufficient chips sent to join the game");
    });
  });

  describe("Game Actions", function() {
    beforeEach(async function() {
      await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
      await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
      await bettingAndPotManagement.connect(owner).startGame();
    });

    it("should allow players to bet", async function() {
      await bettingAndPotManagement.connect(player1).playerAction(4, minimumBet); // PlayerAction.Bet
      const player = await bettingAndPotManagement.players(player1.address);
      expect(player.currentBet).to.equal(minimumBet);
    });

    it("should not allow players to bet less than the minimum", async function() {
      await expect(
        bettingAndPotManagement.connect(player1).playerAction(4, ethers.utils.parseEther("0.05"))
      ).to.be.revertedWith("Bet amount below minimum");
    });
  });

  // Add more unit tests as needed
});

cc: @JW-dev0505 I've adapted for our contracts but please review and update per our needs. Thank you!

peterduhon commented 2 months ago

Integration Testing Framework for Skia Poker Contracts

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker Integration Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");
  const roomId = 0;

  beforeEach(async function() {
    [owner, player1, player2, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      roomId,
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero, // VRF Coordinator (mock for testing)
      ethers.constants.AddressZero, // LINK token (mock for testing)
      ethers.utils.formatBytes32String(""), // keyHash (mock for testing)
      ethers.utils.parseEther("0.1") // fee (mock for testing)
    );

    // Set up the room in RoomManagement
    await roomManagement.createGameRoom("Test Room", minimumBet, 10);
  });

  it("should allow players to join a game room and start the game", async function() {
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");

    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });

    await bettingAndPotManagement.connect(owner).startGame();

    expect(await bettingAndPotManagement.gameState()).to.equal(1); // PreFlop
  });

  it("should handle a full round of betting", async function() {
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");

    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });

    await bettingAndPotManagement.connect(owner).startGame();

    // Player 1 bets
    await bettingAndPotManagement.connect(player1).playerAction(4, minimumBet); // PlayerAction.Bet

    // Player 2 calls
    await bettingAndPotManagement.connect(player2).playerAction(3, minimumBet); // PlayerAction.Call

    // Check that the bets were placed correctly
    const player1Data = await bettingAndPotManagement.players(player1.address);
    const player2Data = await bettingAndPotManagement.players(player2.address);

    expect(player1Data.currentBet).to.equal(minimumBet);
    expect(player2Data.currentBet).to.equal(minimumBet);
  });

  // Add more integration tests as needed
});
peterduhon commented 2 months ago

State Transition Testing for Skia Poker Game

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker State Transition Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");
  const roomId = 0;

  beforeEach(async function() {
    [owner, player1, player2, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      roomId,
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero, // VRF Coordinator (mock for testing)
      ethers.constants.AddressZero, // LINK token (mock for testing)
      ethers.utils.formatBytes32String(""), // keyHash (mock for testing)
      ethers.utils.parseEther("0.1") // fee (mock for testing)
    );

    // Set up the room in RoomManagement
    await roomManagement.createGameRoom("Test Room", minimumBet, 10);

    // Register players
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");

    // Players join the game
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
  });

  it("should transition from WaitingForPlayers to PreFlop when game starts", async function() {
    expect(await bettingAndPotManagement.gameState()).to.equal(0); // WaitingForPlayers

    await bettingAndPotManagement.connect(owner).startGame();

    expect(await bettingAndPotManagement.gameState()).to.equal(1); // PreFlop
  });

  it("should transition through all betting rounds", async function() {
    await bettingAndPotManagement.connect(owner).startGame();
    expect(await bettingAndPotManagement.gameState()).to.equal(1); // PreFlop

    // Simulate PreFlop actions
    await bettingAndPotManagement.connect(player1).playerAction(4, minimumBet); // Bet
    await bettingAndPotManagement.connect(player2).playerAction(3, minimumBet); // Call

    // Move to Flop
    await bettingAndPotManagement.connect(owner).nextGameState();
    expect(await bettingAndPotManagement.gameState()).to.equal(2); // Flop

    // Simulate Flop actions
    await bettingAndPotManagement.connect(player1).playerAction(2, 0); // Check
    await bettingAndPotManagement.connect(player2).playerAction(2, 0); // Check

    // Move to Turn
    await bettingAndPotManagement.connect(owner).nextGameState();
    expect(await bettingAndPotManagement.gameState()).to.equal(3); // Turn

    // Simulate Turn actions
    await bettingAndPotManagement.connect(player1).playerAction(4, minimumBet); // Bet
    await bettingAndPotManagement.connect(player2).playerAction(3, minimumBet); // Call

    // Move to River
    await bettingAndPotManagement.connect(owner).nextGameState();
    expect(await bettingAndPotManagement.gameState()).to.equal(4); // River

    // Simulate River actions
    await bettingAndPotManagement.connect(player1).playerAction(2, 0); // Check
    await bettingAndPotManagement.connect(player2).playerAction(2, 0); // Check

    // Move to Showdown
    await bettingAndPotManagement.connect(owner).nextGameState();
    expect(await bettingAndPotManagement.gameState()).to.equal(5); // Showdown
  });

  // Add more state transition tests as needed
});
peterduhon commented 2 months ago

Edge Case Testing for Skia Poker Contracts

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker Edge Case Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, player3, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");
  const roomId = 0;

  beforeEach(async function() {
    [owner, player1, player2, player3, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      roomId,
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero,
      ethers.constants.AddressZero,
      ethers.utils.formatBytes32String(""),
      ethers.utils.parseEther("0.1")
    );

    // Set up the room in RoomManagement
    await roomManagement.createGameRoom("Test Room", minimumBet, 10);

    // Register players
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");
    await userManagement.connect(player3).registerUser("Player3");
  });

  it("should handle minimum player count (2 players)", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });

    await bettingAndPotManagement.connect(owner).startGame();
    expect(await bettingAndPotManagement.gameState()).to.equal(1); // PreFlop
  });

  it("should handle maximum player count (10 players)", async function() {
    const players = await ethers.getSigners();
    for (let i = 0; i < 10; i++) {
      await userManagement.connect(players[i]).registerUser(`Player${i}`);
      await bettingAndPotManagement.connect(players[i]).joinGame({ value: minimumBet });
    }

    await bettingAndPotManagement.connect(owner).startGame();
    expect(await bettingAndPotManagement.gameState()).to.equal(1); // PreFlop
  });

  it("should reject joining when room is full", async function() {
    // Fill the room to capacity
    const players = await ethers.getSigners();
    for (let i = 0; i < 10; i++) {
      await userManagement.connect(players[i]).registerUser(`Player${i}`);
      await bettingAndPotManagement.connect(players[i]).joinGame({ value: minimumBet });
    }

    // Try to join with an 11th player
    await expect(
      bettingAndPotManagement.connect(players[10]).joinGame({ value: minimumBet })
    ).to.be.revertedWith("Not enough players to start the game");
  });

  it("should handle all players folding except one", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player3).joinGame({ value: minimumBet });

    await bettingAndPotManagement.connect(owner).startGame();

    await bettingAndPotManagement.connect(player1).playerAction(1, 0); // Fold
    await bettingAndPotManagement.connect(player2).playerAction(1, 0); // Fold

    // The game should end with player3 as the winner
    expect(await bettingAndPotManagement.gameState()).to.equal(5); // Showdown
    // You might need to implement a getWinner function to check the winner
  });

  it("should handle a player going all-in", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });

    await bettingAndPotManagement.connect(owner).startGame();

    await bettingAndPotManagement.connect(player1).playerAction(6, minimumBet); // All-in

    const player1Data = await bettingAndPotManagement.players(player1.address);
    expect(player1Data.balance).to.equal(0);
    expect(player1Data.currentBet).to.equal(minimumBet);
  });
});
peterduhon commented 2 months ago

Security Testing for Skia Poker Contracts

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker Security Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, attacker, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");
  const roomId = 0;

  beforeEach(async function() {
    [owner, player1, player2, attacker, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      roomId,
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero,
      ethers.constants.AddressZero,
      ethers.utils.formatBytes32String(""),
      ethers.utils.parseEther("0.1")
    );

    // Set up the room in RoomManagement
    await roomManagement.createGameRoom("Test Room", minimumBet, 10);

    // Register players
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");
  });

  it("should prevent unauthorized access to owner functions", async function() {
    await expect(
      bettingAndPotManagement.connect(attacker).startGame()
    ).to.be.revertedWith("Ownable: caller is not the owner");
  });

  it("should prevent players from betting more than their balance", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(owner).startGame();

    await expect(
      bettingAndPotManagement.connect(player1).playerAction(4, minimumBet.mul(2)) // Bet double the minimum
    ).to.be.revertedWith("Poker Game: Insufficient balance to bet.");
  });

  it("should ensure that only the current player can make a move", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(owner).startGame();

    // Assuming player1 is the first to act
    await expect(
      bettingAndPotManagement.connect(player2).playerAction(4, minimumBet)
    ).to.be.revertedWith("It's not your turn");
  });

  it("should prevent players from joining a game in progress", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(owner).startGame();

    await expect(
      bettingAndPotManagement.connect(attacker).joinGame({ value: minimumBet })
    ).to.be.revertedWith("Game is not accepting new players");
  });

  it("should prevent unauthorized withdrawal from house account", async function() {
    await bettingAndPotManagement.connect(owner).addFundsToHouse({ value: ethers.utils.parseEther("1") });

    await expect(
      bettingAndPotManagement.connect(attacker).withdrawFromHouse(attacker.address, ethers.utils.parseEther("0.5"))
    ).to.be.revertedWith("Not authorized");
  });

  // Add more security tests as needed
});
peterduhon commented 2 months ago

Gas Optimization Testing for Skia Poker Contracts

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Skia Poker Gas Optimization Tests", function() {
  let bettingAndPotManagement, cardManagement, roomManagement, userManagement, aiPlayerManagement;
  let owner, player1, player2, houseAccount;
  const minimumBet = ethers.utils.parseEther("0.1");
  const roomId = 0;

  beforeEach(async function() {
    [owner, player1, player2, houseAccount] = await ethers.getSigners();

    const CardManagement = await ethers.getContractFactory("CardManagement");
    cardManagement = await CardManagement.deploy();

    const RoomManagement = await ethers.getContractFactory("RoomManagement");
    roomManagement = await RoomManagement.deploy();

    const UserManagement = await ethers.getContractFactory("UserManagement");
    userManagement = await UserManagement.deploy();

    const AIPlayerManagement = await ethers.getContractFactory("AIPlayerManagement");
    aiPlayerManagement = await AIPlayerManagement.deploy();

    const BettingAndPotManagement = await ethers.getContractFactory("BettingAndPotManagement");
    bettingAndPotManagement = await BettingAndPotManagement.deploy(
      roomId,
      houseAccount.address,
      cardManagement.address,
      roomManagement.address,
      userManagement.address,
      aiPlayerManagement.address,
      minimumBet,
      ethers.constants.AddressZero,
      ethers.constants.AddressZero,
      ethers.utils.formatBytes32String(""),
      ethers.utils.parseEther("0.1")
    );

    // Set up the room in RoomManagement
    await roomManagement.createGameRoom("Test Room", minimumBet, 10);

    // Register players
    await userManagement.connect(player1).registerUser("Player1");
    await userManagement.connect(player2).registerUser("Player2");
  });

  async function measureGas(transaction) {
    const tx = await transaction;
    const receipt = await tx.wait();
    return receipt.gasUsed;
  }

  it("should optimize gas usage for joining a game", async function() {
    const gasUsed = await measureGas(
      bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet })
    );

    console.log(`Gas used for joining a game: ${gasUsed.toString()}`);
    expect(gasUsed).to.be.below(200000); // Adjust this threshold based on your requirements
  });

  it("should optimize gas usage for starting a game", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });

    const gasUsed = await measureGas(
      bettingAndPotManagement.connect(owner).startGame()
    );

    console.log(`Gas used for starting a game: ${gasUsed.toString()}`);
    expect(gasUsed).to.be.below(300000); // Adjust this threshold based on your requirements
  });

  it("should optimize gas usage for making a bet", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(owner).startGame();

    const gasUsed = await measureGas(
      bettingAndPotManagement.connect(player1).playerAction(4, minimumBet) // Bet
    );

    console.log(`Gas used for making a bet: ${gasUsed.toString()}`);
    expect(gasUsed).to.be.below(100000); // Adjust this threshold based on your requirements
  });

  it("should optimize gas usage for transitioning game states", async function() {
    await bettingAndPotManagement.connect(player1).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(player2).joinGame({ value: minimumBet });
    await bettingAndPotManagement.connect(owner).startGame();

    // Simulate some actions to move to the next state
    await bettingAndPotManagement.connect(player1).playerAction(4, minimumBet); // Bet
    await bettingAndPotManagement.connect(player2).playerAction(3, minimumBet); // Call

    const gasUsed = await measureGas(
      bettingAndPotManagement.connect(owner).nextGameState()
    );

    console.log(`Gas use