Blockchain / Global / Address

Contract

0x7871dbA04da0B2B03a45B76D633AC926275BA151

Balance
9.388228 SOMI
Transactions sent
1
Type
Contract · 13,857 bytes of code

DreamCheckersEscrowNoLimit Verified

Compiler
solc 0.8.30+commit.73712a01
Language
Solidity
Optimization
No
EVM version
—
License
—
Verified
2026-10-12

MainnetDreamCheckers.sol

and 4 more files

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title DreamCheckersEscrowNoLimit
 * @dev Simplified escrow contract for Dream Checkers WITHOUT rate limiting or match timeouts
 * All game logic is handled off-chain, this contract only manages the escrow functions
 */
contract DreamCheckersEscrowNoLimit is ReentrancyGuard, Pausable, Ownable {
    
    // Custom Errors
    error InvalidStake();
    error MatchNotFound();
    error NotAuthorized();
    error InvalidWinner();
    error MatchAlreadyCompleted();
    error TransferFailed();
    error InvalidPlayers();
    error DisputeWindowActive();
    error AdminEarningsOverflow();
    error EmergencyRescueFailed();
    error DisputeWindowExpired();
    error CannotCancelActiveMatch();
    
    // Constants
    uint256 public constant ADMIN_FEE_BPS = 2000; // 20% fee in basis points
    uint256 public constant DISPUTE_WINDOW = 5 minutes; // Time window for dispute after completion
    uint256 public constant MAX_ADMIN_EARNINGS = 1e30; // Prevent overflow
    
    // Enums
    enum MatchStatus {
        WAITING,    // Waiting for second player
        ACTIVE,     // Both players joined, game in progress
        COMPLETED,  // Game finished with winner
        REFUNDED,   // Stakes refunded (draw)
        DISPUTED,   // In dispute state
        CANCELLED   // Match cancelled by creator
    }
    
    // Structs
    struct Match {
        uint256 id;
        address player1;
        address player2;
        uint256 stake;
        MatchStatus status;
        address winner;
        uint256 createdAt;
        uint256 completedAt;
        bool disputeRaised;
    }
    
    // State Variables
    uint256 private _matchCounter;
    uint256 public totalAdminEarnings;
    
    mapping(uint256 => Match) public matches;
    mapping(address => uint256[]) public playerMatches;
    
    // Events
    event MatchCreated(
        uint256 indexed matchId,
        address indexed player1,
        uint256 stake
    );
    
    event MatchJoined(
        uint256 indexed matchId,
        address indexed player1,
        address indexed player2,
        uint256 stake
    );
    
    event MatchCompleted(
        uint256 indexed matchId,
        address indexed winner,
        uint256 winnerPayout,
        uint256 adminFee
    );
    
    event MatchRefunded(
        uint256 indexed matchId,
        address indexed player1,
        address indexed player2,
        uint256 stake
    );
    
    event MatchCancelled(
        uint256 indexed matchId,
        address indexed player1,
        uint256 stake
    );
    
    event MatchDisputed(
        uint256 indexed matchId,
        address indexed disputeRaiser
    );
    
    event AdminFeesWithdrawn(
        address indexed admin,
        uint256 amount
    );
    
    event EmergencyCompletion(
        uint256 indexed matchId,
        address indexed winner,
        string reason
    );
    
    event EmergencyRescue(
        address indexed token,
        address indexed recipient,
        uint256 amount,
        string reason
    );
    
    // Constructor
    constructor(address initialOwner) Ownable(initialOwner) {}
    
    // Modifiers
    
    modifier matchExists(uint256 matchId) {
        if (matchId == 0 || matchId > _matchCounter) revert MatchNotFound();
        _;
    }
    
    modifier onlyMatchPlayer(uint256 matchId) {
        Match storage _match = matches[matchId];
        if (msg.sender != _match.player1 && msg.sender != _match.player2) {
            revert NotAuthorized();
        }
        _;
    }
    
    modifier disputeWindowPassed(uint256 matchId) {
        Match storage _match = matches[matchId];
        if (_match.completedAt + DISPUTE_WINDOW > block.timestamp) {
            revert DisputeWindowActive();
        }
        _;
    }
    
    /**
     * @dev Create a new match with stake deposit (NO TIMEOUTS)
     */
    function createMatch() 
        external 
        payable
        nonReentrant 
        whenNotPaused 
        returns (uint256)
    {
        if (msg.value == 0) revert InvalidStake();
        _matchCounter++;
        
        Match storage newMatch = matches[_matchCounter];
        newMatch.id = _matchCounter;
        newMatch.player1 = msg.sender;
        newMatch.stake = msg.value;
        newMatch.status = MatchStatus.WAITING;
        newMatch.createdAt = block.timestamp;
        
        playerMatches[msg.sender].push(_matchCounter);
        
        emit MatchCreated(_matchCounter, msg.sender, msg.value);
        return _matchCounter;
    }
    
    /**
     * @dev Join an existing match with stake deposit
     */
    function joinMatch(uint256 matchId) 
        external 
        payable
        nonReentrant 
        whenNotPaused 
        matchExists(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Validation
        if (_match.status != MatchStatus.WAITING) revert MatchNotFound();
        if (_match.player1 == msg.sender) revert InvalidPlayers();
        if (msg.value != _match.stake) revert InvalidStake();
        
        // Update match state
        _match.player2 = msg.sender;
        _match.status = MatchStatus.ACTIVE;
        
        playerMatches[msg.sender].push(matchId);
        
        emit MatchJoined(matchId, _match.player1, msg.sender, _match.stake);
    }
    
    /**
     * @dev Cancel a waiting match (creator only, full refund)
     */
    function cancelMatch(uint256 matchId) 
        external 
        nonReentrant 
        matchExists(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Validation
        if (msg.sender != _match.player1) revert NotAuthorized();
        if (_match.status != MatchStatus.WAITING) revert CannotCancelActiveMatch();
        
        // Update match state
        _match.status = MatchStatus.CANCELLED;
        _match.completedAt = block.timestamp;
        
        // Full refund to creator (no fees)
        (bool success, ) = payable(_match.player1).call{value: _match.stake}("");
        if (!success) revert TransferFailed();
        
        emit MatchCancelled(matchId, _match.player1, _match.stake);
    }
    
    /**
     * @dev Complete match with winner (called by authorized backend)
     */
    function completeMatch(uint256 matchId, address winner) 
        external 
        onlyOwner 
        nonReentrant 
        matchExists(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Validation
        if (_match.status != MatchStatus.ACTIVE) revert MatchAlreadyCompleted();
        if (winner != _match.player1 && winner != _match.player2) revert InvalidWinner();
        
        // Update match state
        _match.status = MatchStatus.COMPLETED;
        _match.winner = winner;
        _match.completedAt = block.timestamp;
        
        // Calculate payouts
        uint256 totalPrize = _match.stake * 2;
        uint256 adminFee = (totalPrize * ADMIN_FEE_BPS) / 10000;
        uint256 winnerPayout = totalPrize - adminFee;
        
        // Check for admin earnings overflow
        if (totalAdminEarnings + adminFee > MAX_ADMIN_EARNINGS) {
            revert AdminEarningsOverflow();
        }
        
        // Update admin earnings
        totalAdminEarnings += adminFee;
        
        // Transfer winner payout
        (bool success, ) = payable(winner).call{value: winnerPayout}("");
        if (!success) revert TransferFailed();
        
        emit MatchCompleted(matchId, winner, winnerPayout, adminFee);
    }
    
    /**
     * @dev Handle draw by refunding both players
     */
    function refundDraw(uint256 matchId) 
        external 
        onlyOwner 
        nonReentrant 
        matchExists(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Validation
        if (_match.status != MatchStatus.ACTIVE) revert MatchAlreadyCompleted();
        if (_match.player2 == address(0)) revert InvalidPlayers();
        
        // Update match state
        _match.status = MatchStatus.REFUNDED;
        _match.completedAt = block.timestamp;
        
        // Refund both players (no admin fee on draws)
        (bool success1, ) = payable(_match.player1).call{value: _match.stake}("");
        if (!success1) revert TransferFailed();
        
        (bool success2, ) = payable(_match.player2).call{value: _match.stake}("");
        if (!success2) revert TransferFailed();
        
        emit MatchRefunded(matchId, _match.player1, _match.player2, _match.stake);
    }
    
    /**
     * @dev Raise dispute during dispute window
     */
    function raiseDispute(uint256 matchId) 
        external 
        matchExists(matchId) 
        onlyMatchPlayer(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Validation
        if (_match.status != MatchStatus.COMPLETED) revert MatchNotFound();
        if (_match.completedAt + DISPUTE_WINDOW <= block.timestamp) revert DisputeWindowExpired();
        if (_match.disputeRaised) revert MatchAlreadyCompleted();
        
        // Update match state
        _match.disputeRaised = true;
        _match.status = MatchStatus.DISPUTED;
        
        emit MatchDisputed(matchId, msg.sender);
    }
    
    /**
     * @dev Emergency completion by admin (for stuck matches)
     */
    function emergencyComplete(uint256 matchId, address winner, string calldata reason) 
        external 
        onlyOwner 
        nonReentrant 
        matchExists(matchId) 
    {
        Match storage _match = matches[matchId];
        
        // Can only emergency complete active or disputed matches
        if (_match.status != MatchStatus.ACTIVE && _match.status != MatchStatus.DISPUTED) {
            revert MatchAlreadyCompleted();
        }
        
        // Allow address(0) for draws, but validate non-zero winners
        if (winner != address(0) && winner != _match.player1 && winner != _match.player2) {
            revert InvalidWinner();
        }
        
        _match.status = MatchStatus.COMPLETED;
        _match.winner = winner;
        _match.completedAt = block.timestamp;
        
        if (winner != address(0)) {
            // Winner payout
            uint256 totalPrize = _match.stake * 2;
            uint256 adminFee = (totalPrize * ADMIN_FEE_BPS) / 10000;
            uint256 winnerPayout = totalPrize - adminFee;
            
            // Check for admin earnings overflow
            if (totalAdminEarnings + adminFee > MAX_ADMIN_EARNINGS) {
                revert AdminEarningsOverflow();
            }
            
            totalAdminEarnings += adminFee;
            
            (bool success, ) = payable(winner).call{value: winnerPayout}("");
            if (!success) revert TransferFailed();
        } else {
            // Draw refund - only if both players exist
            if (_match.player2 == address(0)) revert InvalidPlayers();
            
            (bool success1, ) = payable(_match.player1).call{value: _match.stake}("");
            if (!success1) revert TransferFailed();
            
            (bool success2, ) = payable(_match.player2).call{value: _match.stake}("");
            if (!success2) revert TransferFailed();
        }
        
        emit EmergencyCompletion(matchId, winner, reason);
    }
    
    /**
     * @dev Withdraw accumulated admin fees
     */
    function withdrawAdminFees() 
        external 
        onlyOwner 
        nonReentrant 
    {
        uint256 amount = totalAdminEarnings;
        if (amount == 0) revert TransferFailed();
        
        totalAdminEarnings = 0;
        
        (bool success, ) = payable(owner()).call{value: amount}("");
        if (!success) revert TransferFailed();
        
        emit AdminFeesWithdrawn(owner(), amount);
    }
    
    /**
     * @dev Emergency rescue function for stuck tokens or ETH
     * @param token Address of token to rescue (address(0) for ETH)
     * @param amount Amount to rescue
     * @param reason Reason for emergency rescue
     */
    function emergencyRescue(address token, uint256 amount, string calldata reason) 
        external 
        onlyOwner 
        nonReentrant 
    {
        if (token == address(0)) {
            // Rescue ETH
            (bool success, ) = payable(owner()).call{value: amount}("");
            if (!success) revert EmergencyRescueFailed();
        } else {
            // Rescue ERC20 tokens
            (bool success, ) = token.call(abi.encodeWithSignature("transfer(address,uint256)", owner(), amount));
            if (!success) revert EmergencyRescueFailed();
        }
        
        emit EmergencyRescue(token, owner(), amount, reason);
    }
    
    // View Functions
    
    /**
     * @dev Get match details
     */
    function getMatch(uint256 matchId) 
        external 
        view 
        matchExists(matchId) 
        returns (Match memory) 
    {
        return matches[matchId];
    }
    
    /**
     * @dev Get player's match history
     */
    function getPlayerMatches(address player) 
        external 
        view 
        returns (uint256[] memory) 
    {
        return playerMatches[player];
    }
    
    /**
     * @dev Get current match counter
     */
    function getCurrentMatchId() external view returns (uint256) {
        return _matchCounter;
    }
    
    /**
     * @dev Check if match can be completed (dispute window passed)
     */
    function canFinalizeMatch(uint256 matchId) 
        external 
        view 
        matchExists(matchId) 
        returns (bool) 
    {
        Match storage _match = matches[matchId];
        return _match.status == MatchStatus.COMPLETED && 
               _match.completedAt + DISPUTE_WINDOW <= block.timestamp;
    }
    
    /**
     * @dev Get contract balance of ETH
     */
    function getETHBalance() external view returns (uint256) {
        return address(this).balance;
    }
    
    /**
     * @dev Emergency pause function
     */
    function pause() external onlyOwner {
        _pause();
    }
    
    /**
     * @dev Emergency unpause function
     */
    function unpause() external onlyOwner {
        _unpause();
    }
    
    // Fallback function to receive ETH
    receive() external payable {}
}

Other files

  • @openzeppelin/contracts/access/Ownable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    
    pragma solidity ^0.8.20;
    
    import {Context} from "../utils/Context.sol";
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
    
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
    
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    
  • @openzeppelin/contracts/security/Pausable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
    
    pragma solidity ^0.8.0;
    
    import "../utils/Context.sol";
    
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
    
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
    
        bool private _paused;
    
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
    
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
    
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            require(!paused(), "Pausable: paused");
        }
    
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            require(paused(), "Pausable: not paused");
        }
    
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
    
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    
  • @openzeppelin/contracts/security/ReentrancyGuard.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
    
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
    
        uint256 private _status;
    
        constructor() {
            _status = _NOT_ENTERED;
        }
    
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
    
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
    
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == _ENTERED;
        }
    }
    
  • @openzeppelin/contracts/utils/Context.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    
ABI · 54 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "initialOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "AdminEarningsOverflow",
    "type": "error",
    "inputs": []
  },
  {
    "name": "CannotCancelActiveMatch",
    "type": "error",
    "inputs": []
  },
  {
    "name": "DisputeWindowActive",
    "type": "error",
    "inputs": []
  },
  {
    "name": "DisputeWindowExpired",
    "type": "error",
    "inputs": []
  },
  {
    "name": "EmergencyRescueFailed",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidPlayers",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidStake",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidWinner",
    "type": "error",
    "inputs": []
  },
  {
    "name": "MatchAlreadyCompleted",
    "type": "error",
    "inputs": []
  },
  {
    "name": "MatchNotFound",
    "type": "error",
    "inputs": []
  },
  {
    "name": "NotAuthorized",
    "type": "error",
    "inputs": []
  },
  {
    "name": "OwnableInvalidOwner",
    "type": "error",
    "inputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "OwnableUnauthorizedAccount",
    "type": "error",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "TransferFailed",
    "type": "error",
    "inputs": []
  },
  {
    "name": "AdminFeesWithdrawn",
    "type": "event",
    "inputs": [
      {
        "name": "admin",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "EmergencyCompletion",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "winner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "reason",
        "type": "string",
        "indexed": false,
        "internalType": "string"
      }
    ],
    "anonymous": false
  },
  {
    "name": "EmergencyRescue",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "recipient",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "reason",
        "type": "string",
        "indexed": false,
        "internalType": "string"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchCancelled",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "player1",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "stake",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchCompleted",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "winner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "winnerPayout",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "adminFee",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchCreated",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "player1",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "stake",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchDisputed",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "disputeRaiser",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchJoined",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "player1",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "player2",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "stake",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MatchRefunded",
    "type": "event",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "player1",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "player2",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "stake",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipTransferred",
    "type": "event",
    "inputs": [
      {
        "name": "previousOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Paused",
    "type": "event",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Unpaused",
    "type": "event",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ADMIN_FEE_BPS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "DISPUTE_WINDOW",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "MAX_ADMIN_EARNINGS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "canFinalizeMatch",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "cancelMatch",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "completeMatch",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "winner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "createMatch",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "payable"
  },
  {
    "name": "emergencyComplete",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "winner",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "reason",
        "type": "string",
        "internalType": "string"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "emergencyRescue",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "reason",
        "type": "string",
        "internalType": "string"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "getCurrentMatchId",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getETHBalance",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getMatch",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "tuple",
        "components": [
          {
            "name": "id",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "player1",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "player2",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "stake",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "status",
            "type": "uint8",
            "internalType": "enum DreamCheckersEscrowNoLimit.MatchStatus"
          },
          {
            "name": "winner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "createdAt",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "completedAt",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "disputeRaised",
            "type": "bool",
            "internalType": "bool"
          }
        ],
        "internalType": "struct DreamCheckersEscrowNoLimit.Match"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getPlayerMatches",
    "type": "function",
    "inputs": [
      {
        "name": "player",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "joinMatch",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "matches",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "id",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "player1",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "player2",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "stake",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "status",
        "type": "uint8",
        "internalType": "enum DreamCheckersEscrowNoLimit.MatchStatus"
      },
      {
        "name": "winner",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "createdAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "completedAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "disputeRaised",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "owner",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "pause",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "paused",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "playerMatches",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "raiseDispute",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "refundDraw",
    "type": "function",
    "inputs": [
      {
        "name": "matchId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "renounceOwnership",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "totalAdminEarnings",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "transferOwnership",
    "type": "function",
    "inputs": [
      {
        "name": "newOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "unpause",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "withdrawAdminFees",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "receive",
    "stateMutability": "payable"
  }
]