creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/Coinflip.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Coin} from './Coinflip/Coin.sol';\nimport {Game} from './Coinflip/Game.sol';\nimport {UsingGamePlays} from './Coinflip/GamePlays.sol';\nimport {UsingGameStatuses} from './Coinflip/GameStatuses.sol';\n\nimport {Wallets} from './Wallets.sol';\nimport {Ownable} from './Ownable.sol';\nimport {MaybeOperational} from './MaybeOperational.sol';\nimport {UsingServiceProvider} from './ServiceProvider.sol';\n\ncontract Coinflip is\n    UsingGamePlays,\n    UsingGameStatuses,\n    Ownable,\n    MaybeOperational,\n    UsingServiceProvider\n{\n    uint public minWager;\n    mapping(uint gameID => uint wager) public wagers;\n    uint16 public maxNumberOfPlayers;\n    uint public gamesCount;\n    Wallets public wallets;\n\n    event GameCreated(\n        uint indexed gameID,\n        address indexed creator,\n        uint16 numberOfPlayers,\n        uint expiryTimestamp,\n        uint wager\n    );\n    event GamePlayChanceRevealed(\n        uint indexed gameID,\n        uint16 indexed gamePlayID,\n        bytes chanceAndSalt\n    );\n    event GameCompleted(\n        uint indexed gameID,\n        Coin.Side coinSide,\n        uint amountForEachWinner\n    );\n    event ExpiredGameRefunded(\n        uint indexed gameID,\n        uint refundedAmountPerPlayer\n    );\n    event GameExpiryAdjusted(uint indexed gameID, uint expiryTimestamp);\n\n    error IncorrectGameWager();\n    error MinimumWagerNotMet();\n    error InvalidProofOfChance();\n    error MaxNumberOfPlayersError();\n\n    constructor(\n        address payable wallets_,\n        uint16 maxNumberOfPlayers_,\n        uint minWager_\n    ) {\n        wallets = Wallets(wallets_);\n        maxNumberOfPlayers = maxNumberOfPlayers_;\n        minWager = minWager_;\n    }\n\n    /// @notice Coinflip tops up your wallet balance when it receives any ether value\n    receive() external payable {\n        wallets.creditAccount{value: msg.value}(msg.sender);\n    }\n\n    /// @notice Allow updating Wallets contract in case a PPV is ever discovered\n    function updateWallets(address payable wallets_) external onlyOwner {\n        wallets = Wallets(wallets_);\n    }\n\n    /// @notice Updates max number of players to avoid concluding games with very high gas fee\n    function updateMaxNumberOfPlayers(\n        uint16 maxNumberOfPlayers_\n    ) external onlyOwner {\n        maxNumberOfPlayers = maxNumberOfPlayers_;\n    }\n\n    /// @notice Updates min wager allowed\n    function updateMinWager(uint minWager_) external onlyOwner {\n        minWager = minWager_;\n    }\n\n    /// @notice Creates a new game\n    /// @param numberOfPlayers number of participating players\n    /// @param expiryTimestamp Expiry timestamp of the game\n    /// @param coinSide predicted coin side by you, the creator\n    /// @param proofOfChance SHA256 hash of your chance (lucky word[s]) and a random salt combined\n    function createGame(\n        uint16 numberOfPlayers,\n        uint expiryTimestamp,\n        Coin.Side coinSide,\n        bytes32 proofOfChance\n    ) external payable mustBeOperational {\n        uint wager = msg.value;\n        if (wager < minWager) {\n            revert MinimumWagerNotMet();\n        }\n\n        require(numberOfPlayers >= Coin.TOTAL_SIDES_COUNT);\n\n        if (numberOfPlayers > maxNumberOfPlayers) {\n            revert MaxNumberOfPlayersError();\n        }\n\n        uint newGameID = ++gamesCount;\n        wagers[newGameID] = wager;\n        setNumberOfPlayers(newGameID, numberOfPlayers);\n        setGameExpiry(newGameID, expiryTimestamp);\n\n        address player = msg.sender;\n        emit GameCreated(\n            newGameID,\n            player,\n            numberOfPlayers,\n            expiryTimestamp,\n            wager\n        );\n        createGamePlay(player, newGameID, coinSide, proofOfChance);\n    }\n\n    /// @notice Allows playing an already created game\n    /// @param gameID already created game ID\n    /// @param coinSide coin side outcome prediction\n    /// @param proofOfChance SHA256 hash of your chance (lucky word[s]) and a random salt combined\n    function playGame(\n        uint gameID,\n        Coin.Side coinSide,\n        bytes32 proofOfChance\n    )\n        external\n        payable\n        mustBeOperational\n        mustMatchGameStatus(gameID, Game.Status.AwaitingPlayers)\n        mustAvoidAllGamePlaysMatching(gameID, coinSide)\n        mustAvoidPlayingAgain(gameID)\n    {\n        uint wager = wagers[gameID];\n        if (msg.value != wager) {\n            revert IncorrectGameWager();\n        }\n        createGamePlay(msg.sender, gameID, coinSide, proofOfChance);\n        maybeSetGameStatusAsAwaitingChancesReveal(gameID);\n    }\n\n    /// @notice Reveals the chances (lucky words) of all plays for a given game.\n    /// After, it computes and stores the coinflip outcome for the given game.\n    /// Then, it credits players that predicted the coinflip outcome correctly with the combined\n    /// wager shared equally (after service charge deduction)\n    /// @param gameID already created game ID\n    /// @param chanceAndSalts list of the chance and salts combined in the order of their respecitive play IDs\n    function revealChancesAndCreditWinners(\n        uint gameID,\n        bytes[] calldata chanceAndSalts\n    )\n        external\n        onlyOwner\n        mustMatchGameStatus(gameID, Game.Status.AwaitingChancesReveal)\n    {\n        Coin.Side flipOutcome;\n        for (uint16 i; i < numberOfPlayersForGameWith[gameID]; ) {\n            bytes calldata chanceAndSalt = chanceAndSalts[i];\n\n            uint16 gamePlayID = i + 1;\n\n            if (sha256(chanceAndSalt) != proofOfChances[gameID][gamePlayID]) {\n                revert InvalidProofOfChance();\n            }\n\n            (bytes16 chance, ) = abi.decode(chanceAndSalt, (bytes16, bytes8));\n\n            for (uint8 j; j < 16; ) {\n                bytes1 chance_character = chance[j];\n                if (chance_character == 0) {\n                    break;\n                }\n\n                unchecked {\n                    if (flipOutcome == Coin.Side.Head) {\n                        flipOutcome = Coin.Side.Tail;\n                    } else {\n                        flipOutcome = Coin.Side.Head;\n                    }\n\n                    ++j;\n                }\n            }\n            emit GamePlayChanceRevealed(gameID, gamePlayID, chanceAndSalt);\n            unchecked {\n                ++i;\n            }\n        }\n\n        address[] memory winners = players[gameID][flipOutcome];\n        uint amountForEachWinner = creditGameWinners(gameID, winners);\n        setGameStatus(gameID, Game.Status.Concluded);\n        emit GameCompleted(gameID, flipOutcome, amountForEachWinner);\n    }\n\n    /// @notice Batch refunds expired game players\n    /// @param gameIDs game IDs of expired games\n    /// @dev Everyone's authorized to remove the possibility of locking\n    /// wagers forever if an owner loses access to their private keys\n    function refundExpiredGamePlayersForGames(\n        uint[] calldata gameIDs\n    ) external {\n        for (uint8 i; i < gameIDs.length; ) {\n            _refundExpiredGamePlayers(gameIDs[i]);\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /// @notice Refund wagers of an expired game to its players\n    /// @param gameID game ID of expired game\n    function refundExpiredGamePlayers(uint gameID) external {\n        _refundExpiredGamePlayers(gameID);\n    }\n\n    function adjustExpiryForGame(\n        uint gameID,\n        uint newExpiryTimestamp\n    ) external onlyOwner {\n        _adjustExpiryForGame(gameID, newExpiryTimestamp);\n    }\n\n    function adjustExpiryForGames(\n        uint[] calldata gameIDs,\n        uint newExpiryTimestamp\n    ) external onlyOwner {\n        for (uint8 i; i < gameIDs.length; ) {\n            uint gameID = gameIDs[i];\n\n            _adjustExpiryForGame(gameID, newExpiryTimestamp);\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function _adjustExpiryForGame(\n        uint gameID,\n        uint newExpiryTimestamp\n    ) private {\n        Game.Status gameStatus = getGameStatus(gameID);\n        require(\n            gameStatus == Game.Status.AwaitingPlayers ||\n                gameStatus == Game.Status.AwaitingChancesReveal\n        );\n\n        setGameExpiry(gameID, newExpiryTimestamp);\n        emit GameExpiryAdjusted(gameID, newExpiryTimestamp);\n    }\n\n    /// @notice returns the ether balance of this contract i.e. total wagers staked\n    function getTotalBalance() external view returns (uint) {\n        return address(this).balance;\n    }\n\n    function creditGameWinners(\n        uint gameID,\n        address[] memory winners\n    ) private returns (uint amountForEachWinner) {\n        uint gameWager = wagers[gameID];\n        uint totalWager = gameWager * playCountsSoFar[gameID];\n        (\n            uint amountForEachPlayer,\n            uint serviceChargeAmount\n        ) = getSplitAndServiceChargeAmounts(totalWager, winners.length);\n        wallets.creditManyAndOne{value: totalWager}(\n            winners,\n            amountForEachPlayer,\n            owner(),\n            serviceChargeAmount\n        );\n        return amountForEachPlayer;\n    }\n\n    function _refundExpiredGamePlayers(\n        uint gameID\n    ) private mustMatchGameStatus(gameID, Game.Status.Expired) {\n        address[] memory allPlayers = allPlayers[gameID];\n        uint16 allPlayersLength = uint16(allPlayers.length);\n        uint totalWager = wagers[gameID] * allPlayersLength;\n        (\n            uint refundAmountPerPlayer,\n            uint serviceChargeAmount\n        ) = getSplitAndServiceChargeAmounts(totalWager, allPlayersLength);\n        wallets.creditManyAndOne{value: totalWager}(\n            allPlayers,\n            refundAmountPerPlayer,\n            owner(),\n            serviceChargeAmount\n        );\n        setGameStatus(gameID, Game.Status.Concluded);\n        emit ExpiredGameRefunded(gameID, refundAmountPerPlayer);\n    }\n\n    function maybeSetGameStatusAsAwaitingChancesReveal(uint gameID) private {\n        uint16 playCount = playCountsSoFar[gameID];\n        uint16 numberOfPlayers = numberOfPlayersForGameWith[gameID];\n\n        if (playCount == numberOfPlayers) {\n            setGameStatus(gameID, Game.Status.AwaitingChancesReveal);\n        }\n    }\n}\n"
      },
      "src/Coinflip/Coin.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nlibrary Coin {\n    enum Side {\n        Head,\n        Tail\n    }\n\n    uint8 public constant TOTAL_SIDES_COUNT = 2;\n}\n"
      },
      "src/Coinflip/Game.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Coin} from './Coin.sol';\n\nlibrary Game {\n    enum Status {\n        AwaitingPlayers,\n        AwaitingChancesReveal,\n        Expired,\n        /// Concluded games are Completed/Expired games have been paid_out/refunded\n        Concluded\n    }\n}\n"
      },
      "src/Coinflip/GamePlays.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Coin} from './Coin.sol';\nimport {Game} from './Game.sol';\n\ncontract UsingGamePlays {\n    mapping(uint gameID => mapping(uint16 playID => bytes32 proofOfChance))\n        public proofOfChances;\n    mapping(uint gameID => uint16 playCount) public playCountsSoFar;\n    mapping(uint gameID => uint16 numberOfPlayers)\n        public numberOfPlayersForGameWith;\n    mapping(uint gameID => mapping(Coin.Side coinSide => address[] player))\n        public players;\n    mapping(uint gameID => address[] player) public allPlayers;\n\n    mapping(uint gameID => mapping(address player => uint16 playID))\n        private playRecord;\n    mapping(uint gameID => mapping(Coin.Side coinSide => uint16 coinSideCount))\n        private coinSideCounts;\n\n    event GamePlayCreated(\n        uint indexed gameID,\n        uint16 indexed gamePlayID,\n        address indexed player,\n        Coin.Side coinSide,\n        bytes32 proofOfChance\n    );\n\n    error AllMatchingPlaysError(Coin.Side availableCoinSide);\n    error AlreadyPlayedError(uint16 playID);\n\n    modifier mustAvoidAllGamePlaysMatching(uint gameID, Coin.Side coinSide) {\n        uint16 playsLeft = numberOfPlayersForGameWith[gameID] -\n            playCountsSoFar[gameID];\n        uint16 headPlayCount = coinSideCounts[gameID][Coin.Side.Head];\n        uint16 tailPlayCount = coinSideCounts[gameID][Coin.Side.Tail];\n\n        if (playsLeft == 1 && (headPlayCount == 0 || tailPlayCount == 0)) {\n            Coin.Side availableCoinSide;\n            if (headPlayCount == 0) {\n                availableCoinSide = Coin.Side.Head;\n            } else {\n                availableCoinSide = Coin.Side.Tail;\n            }\n\n            if (coinSide != availableCoinSide) {\n                revert AllMatchingPlaysError(availableCoinSide);\n            }\n        }\n\n        _;\n    }\n\n    /// @notice Curbs ambiguity when players prove their chances\n    modifier mustAvoidPlayingAgain(uint gameID) {\n        uint16 myPlayID = playRecord[gameID][msg.sender];\n\n        if (myPlayID > 0) {\n            revert AlreadyPlayedError(myPlayID);\n        }\n\n        _;\n    }\n\n    function createGamePlay(\n        address player,\n        uint gameID,\n        Coin.Side coinSide,\n        bytes32 proofOfChance\n    ) internal {\n        uint16 gamePlayID = playCountsSoFar[gameID] + 1;\n        playRecord[gameID][player] = gamePlayID;\n        proofOfChances[gameID][gamePlayID] = proofOfChance;\n        players[gameID][coinSide].push(player);\n        allPlayers[gameID].push(player);\n        coinSideCounts[gameID][coinSide]++;\n        playCountsSoFar[gameID]++;\n\n        emit GamePlayCreated(\n            gameID,\n            gamePlayID,\n            player,\n            coinSide,\n            proofOfChance\n        );\n    }\n\n    function setNumberOfPlayers(uint gameID, uint16 numberOfPlayers) internal {\n        numberOfPlayersForGameWith[gameID] = numberOfPlayers;\n    }\n}\n"
      },
      "src/Coinflip/GameStatuses.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Game} from './Game.sol';\n\ncontract UsingGameStatuses {\n    mapping(uint gameID => uint expiryTimestamp) public expiryTimestamps;\n\n    mapping(uint gameID => Game.Status) private statuses;\n\n    error InvalidGameStatus(\n        uint gameID,\n        Game.Status expected,\n        Game.Status actual\n    );\n    error InvalidExpiryTimestamp();\n\n    modifier mustMatchGameStatus(uint gameID, Game.Status expectedGameStatus) {\n        Game.Status actualGameStatus = getGameStatus(gameID);\n\n        if (actualGameStatus != expectedGameStatus) {\n            revert InvalidGameStatus(\n                gameID,\n                expectedGameStatus,\n                actualGameStatus\n            );\n        }\n\n        _;\n    }\n\n    function setGameExpiry(uint gameID, uint expiryTimestamp) internal {\n        if (expiryTimestamp <= block.timestamp) {\n            revert InvalidExpiryTimestamp();\n        }\n        expiryTimestamps[gameID] = expiryTimestamp;\n    }\n\n    function setGameStatus(uint gameID, Game.Status status) internal {\n        statuses[gameID] = status;\n    }\n\n    function getGameStatus(uint gameID) public view returns (Game.Status) {\n        Game.Status status = statuses[gameID];\n\n        bool isAwaiting = status == Game.Status.AwaitingPlayers ||\n            status == Game.Status.AwaitingChancesReveal;\n        bool isExpired = expiryTimestamps[gameID] < block.timestamp;\n\n        if (isAwaiting && isExpired) {\n            return Game.Status.Expired;\n        } else {\n            return status;\n        }\n    }\n}\n"
      },
      "src/MaybeOperational.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Ownable} from './Ownable.sol';\n\ncontract MaybeOperational is Ownable {\n    bool private isOperational = true;\n\n    error InOperational();\n\n    /**\n     * @dev Modifier that requires the \"operational\" boolean variable to be \"true\"\n     *      This is used on all state changing functions to pause the contract in\n     *      the event there is an issue that needs to be fixed\n     */\n    modifier mustBeOperational() {\n        if (!isOperational) {\n            revert InOperational();\n        }\n        _;\n    }\n\n    /**\n     * @dev Sets contract operations on/off\n     *\n     * When isOperational is false, all affected write transactions except for this one will fail\n     */\n    function setIsOperational(bool isOperational_) external onlyOwner {\n        isOperational = isOperational_;\n    }\n}\n"
      },
      "src/Ownable.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nabstract contract Ownable {\n    address private _owner;\n\n    /// The caller account is not authorized to perform an operation.\n    error OwnableUnauthorizedAccount(address account);\n\n    event OwnershipTransferred(\n        address indexed oldOwner,\n        address indexed newOwner\n    );\n\n    /// @dev The owner is not a valid owner account.\n    error OwnableInvalidOwner(address owner);\n\n    constructor() {\n        _transferOwnership(msg.sender);\n    }\n\n    /// @dev Throws if called by any account other than the owner.\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /// @dev Returns the address of the current owner.\n    function owner() public view returns (address) {\n        return _owner;\n    }\n\n    /// @dev Throws if the sender is not the owner.\n    function _checkOwner() internal view {\n        if (owner() != msg.sender) {\n            revert OwnableUnauthorizedAccount(msg.sender);\n        }\n    }\n\n    /// @dev Transfers ownership of the contract to a new account (`newOwner`).\n    /// Can only be called by the current owner.\n    function transferOwnership(address newOwner) public onlyOwner {\n        _transferOwnership(newOwner);\n    }\n\n    /// @dev Transfers ownership of the contract to a new account (`newOwner`).\n    /// Internal function without access restriction.\n    function _transferOwnership(address newOwner) internal {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "src/ServiceProvider.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Ownable} from './Ownable.sol';\n\ncontract UsingServiceProvider is Ownable {\n    /// @dev Factor-in gas prices, min-wager & operations cost\n    uint8 public serviceChargePercent = 8;\n\n    error InvalidServiceChargePercent();\n\n    /// @dev Allows owner update service charge percent\n    function updateServiceChargePercent(\n        uint8 serviceChargePercent_\n    ) external onlyOwner {\n        if (serviceChargePercent_ >= 100) {\n            revert InvalidServiceChargePercent();\n        }\n\n        serviceChargePercent = serviceChargePercent_;\n    }\n\n    /// @dev Returns the service provider wallet address\n    function getServiceProviderWallet() external view returns (address) {\n        return owner();\n    }\n\n    function getServiceCharge(uint amount) external view returns (uint) {\n        return (amount * serviceChargePercent) / 100;\n    }\n\n    /// @notice Made public to allow auditing games transparently\n    /// @dev Returns the `splitAmount` for each `place` and the `serviceChargeAmount`\n    function getSplitAndServiceChargeAmounts(\n        uint totalAmount,\n        uint places\n    ) public view returns (uint, uint) {\n        uint splitAmount = totalAmount / places;\n        splitAmount =\n            splitAmount -\n            ((splitAmount * serviceChargePercent) / 100);\n\n        uint serviceChargeAmount = totalAmount - (splitAmount * places);\n\n        return (splitAmount, serviceChargeAmount);\n    }\n}\n"
      },
      "src/Wallets.sol": {
        "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity 0.8.25;\n\nimport {Ownable} from './Ownable.sol';\nimport {UsingReentrancyGuard} from './Wallets/ReentrancyGuard.sol';\n\ncontract Wallets is UsingReentrancyGuard {\n    mapping(address account => uint amount) balances;\n\n    event Credit(address indexed account, uint amount);\n    event Debit(address indexed account, uint amount);\n\n    error InsufficientFunds();\n\n    receive() external payable {\n        _credit(msg.sender, msg.value);\n    }\n\n    /// @notice Credits account. You can credit someone using this.\n    function creditAccount(address account) external payable {\n        _credit(account, msg.value);\n    }\n\n    /// @notice Credits your account.\n    function credit() external payable {\n        _credit(msg.sender, msg.value);\n    }\n\n    /// @dev Credits accounts specified here\n    function creditAccounts(address[] calldata accounts) external payable {\n        uint amount = msg.value;\n        uint accountsLength = accounts.length;\n        require(amount % accountsLength == 0);\n        uint amountForEachAccount = amount / accountsLength;\n        for (uint i; i < accountsLength; ) {\n            _credit(accounts[i], amountForEachAccount);\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /// @notice Same behavior as `creditAccounts` but credits\n    /// `manyAccounts` using `amountForEachManyAccount` and\n    /// `oneAccount` using `amountForOneAccount`\n    function creditManyAndOne(\n        address[] calldata manyAccounts,\n        uint amountForEachManyAccount,\n        address oneAccount,\n        uint amountForOneAccount\n    ) external payable {\n        uint manyAccountsLength = manyAccounts.length;\n        require(\n            (amountForEachManyAccount * manyAccountsLength) +\n                amountForOneAccount ==\n                msg.value\n        );\n\n        for (uint i; i < manyAccountsLength; ) {\n            _credit(manyAccounts[i], amountForEachManyAccount);\n            unchecked {\n                ++i;\n            }\n        }\n\n        _credit(oneAccount, amountForOneAccount);\n    }\n\n    /// @notice Allows withdrawing a specified amount from your wallet balance\n    function withdraw(uint amount) external nonReentrant {\n        address account = msg.sender;\n        uint balance = balances[account];\n        if (balance < amount) {\n            revert InsufficientFunds();\n        }\n        balances[account] -= amount;\n\n        _pay(account, amount);\n\n        emit Debit(account, amount);\n    }\n\n    /// @notice Allows withdrawing all amount from your wallet balance\n    function withdrawAll() external nonReentrant {\n        address account = msg.sender;\n        uint balance = balances[account];\n        if (balance == 0) {\n            revert InsufficientFunds();\n        }\n        balances[account] = 0;\n\n        _pay(account, balance);\n\n        emit Debit(account, balance);\n    }\n\n    /// @notice returns the balance of a wallet account\n    function getBalance(address account) external view returns (uint) {\n        return balances[account];\n    }\n\n    /// @notice returns the balance of this wallet contract in wei\n    function getTotalBalance() external view returns (uint) {\n        return address(this).balance;\n    }\n\n    function _credit(address account, uint amount) private {\n        balances[account] += amount;\n        emit Credit(account, amount);\n    }\n\n    function _pay(address to, uint256 amount) private {\n        (bool sent, ) = to.call{value: amount}('');\n        require(sent);\n    }\n}\n"
      },
      "src/Wallets/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)\n\npragma solidity 0.8.25;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract UsingReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    constructor() {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be _NOT_ENTERED\n        if (_status == _ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _status == _ENTERED;\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 1000
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {}
    }
  }}