OPFaultGameFinder

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/OPFaultGameFinder.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

// https://github.com/ethereum-optimism/optimism/issues/11269

// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol
interface IOptimismPortal {
    function disputeGameFactory() external view returns (IDisputeGameFactory);
    function respectedGameType() external view returns (uint256);
    function disputeGameBlacklist(
        IDisputeGame game
    ) external view returns (bool);
    function disputeGameFinalityDelaySeconds() external view returns (uint256);
    function respectedGameTypeUpdatedAt() external view returns (uint64);
}

// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol
interface IDisputeGameFactory {
    function gameCount() external view returns (uint256);
    function gameAtIndex(
        uint256 index
    )
        external
        view
        returns (uint256 gameType, uint256 created, IDisputeGame gameProxy);
}

// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol
interface IDisputeGame {
    function status() external view returns (uint256);
    function l2BlockNumber() external view returns (uint256);
    function rootClaim() external view returns (bytes32);
    function resolvedAt() external view returns (uint64);
}

struct FinalizationParams {
    uint256 finalityDelay;
    uint64 gameTypeUpdatedAt;
}

// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/dispute/lib/Types.sol#L7
uint256 constant CHALLENGER_WINS = 1;
uint256 constant DEFENDER_WINS = 2;

// https://github.com/ethereum-optimism/optimism/blob/42acc178b262b4cdcc75f9b3f4f63941c65bcb8a/packages/contracts-bedrock/interfaces/dispute/IFaultDisputeGame.sol
interface IFaultDisputeGame {
    function l2BlockNumberChallenged() external view returns (bool);
}

// https://github.com/ethereum-optimism/optimism/blob/42acc178b262b4cdcc75f9b3f4f63941c65bcb8a/packages/contracts-bedrock/src/dispute/lib/Types.sol
uint32 constant GAME_TYPE_CANNON = 0;
uint32 constant GAME_TYPE_PERMISSIONED_CANNON = 1;

error GameNotFound();

contract OPFaultGameFinder {
    function findGameIndex(
        IOptimismPortal portal,
        uint256 minAgeSec,
        uint256[] memory allowedGameTypes,
        uint256 gameCount
    ) external view virtual returns (uint256) {
        FinalizationParams memory finalizationParams = FinalizationParams({
            finalityDelay: portal.disputeGameFinalityDelaySeconds(),
            gameTypeUpdatedAt: portal.respectedGameTypeUpdatedAt()
        });
        IDisputeGameFactory factory = portal.disputeGameFactory();
        if (gameCount == 0) gameCount = factory.gameCount();
        while (gameCount > 0) {
            (
                uint256 gameType,
                uint256 created,
                IDisputeGame gameProxy
            ) = factory.gameAtIndex(--gameCount);
            if (
                _isGameUsable(
                    portal,
                    gameProxy,
                    gameType,
                    created,
                    allowedGameTypes,
                    minAgeSec,
                    finalizationParams
                )
            ) {
                return gameCount;
            }
        }
        revert GameNotFound();
    }

    function gameAtIndex(
        IOptimismPortal portal,
        uint256 minAgeSec,
        uint256[] memory allowedGameTypes,
        uint256 gameIndex
    )
        external
        view
        returns (
            uint256 gameType,
            uint256 created,
            IDisputeGame gameProxy,
            uint256 l2BlockNumber,
            bytes32 rootClaim
        )
    {
        FinalizationParams memory finalizationParams = FinalizationParams({
            finalityDelay: portal.disputeGameFinalityDelaySeconds(),
            gameTypeUpdatedAt: portal.respectedGameTypeUpdatedAt()
        });
        IDisputeGameFactory factory = portal.disputeGameFactory();
        (gameType, created, gameProxy) = factory.gameAtIndex(gameIndex);
        if (
            _isGameUsable(
                portal,
                gameProxy,
                gameType,
                created,
                allowedGameTypes,
                minAgeSec,
                finalizationParams
            )
        ) {
            l2BlockNumber = gameProxy.l2BlockNumber();
            rootClaim = gameProxy.rootClaim();
        }
    }

    function _isGameUsable(
        IOptimismPortal portal,
        IDisputeGame gameProxy,
        uint256 gameType,
        uint256 created,
        uint256[] memory allowedGameTypes,
        uint256 minAgeSec,
        FinalizationParams memory finalizationParams
    ) internal view returns (bool) {
        if (!_isAllowedGameType(gameType, allowedGameTypes)) return false;
        // https://specs.optimism.io/fault-proof/stage-one/bridge-integration.html#blacklisting-disputegames
        if (portal.disputeGameBlacklist(gameProxy)) return false;
        if (minAgeSec > 0) {
            if (created > block.timestamp - minAgeSec) return false;
            if (
                gameType == GAME_TYPE_CANNON ||
                gameType == GAME_TYPE_PERMISSIONED_CANNON
            ) {
                if (
                    IFaultDisputeGame(address(gameProxy))
                        .l2BlockNumberChallenged()
                ) return gameProxy.status() == DEFENDER_WINS;

                return true;
            }
            // Testing for an unchallenged game falls back to finalized mode if unknown game type
        }

        if (
            created > finalizationParams.gameTypeUpdatedAt &&
            gameProxy.status() == DEFENDER_WINS
        ) {
            return ((block.timestamp - gameProxy.resolvedAt()) >
                finalizationParams.finalityDelay);
        }
        return false;
    }


    function _isAllowedGameType(uint256 gameType, uint256[] memory allowedGameTypes) pure internal returns (bool) {
        for (uint i = 0; i < allowedGameTypes.length; i++) {
            if (allowedGameTypes[i] == gameType) return true;
        }
        return false;
    }
}
"
    }
  },
  "settings": {
    "remappings": [
      "@unruggable/=lib/unruggable-gateways/",
      "@eth-optimism/=lib/unruggable-gateways/lib/optimism/packages/",
      "@openzeppelin/contracts/=lib/unruggable-gateways/lib/openzeppelin-contracts/contracts/",
      "ds-test/=lib/unruggable-gateways/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/unruggable-gateways/lib/openzeppelin-contracts/lib/erc4626-tests/",
      "forge-std/=lib/unruggable-gateways/lib/forge-std/src/",
      "openzeppelin-contracts/=lib/unruggable-gateways/lib/openzeppelin-contracts/",
      "optimism/=lib/unruggable-gateways/lib/optimism/packages/contracts-bedrock/src/",
      "unruggable-gateways/=lib/unruggable-gateways/contracts/"
    ],
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": false
  }
}}

Tags:
Factory|addr:0x7512acaac95c0f4a934cf227cca22fe784f35adc|verified:true|block:23468258|tx:0xa17a74d39c6f530442ca09cf812ed8439754f663dbb0e30710442822641b9f31|first_check:1759147827

Submitted on: 2025-09-29 14:10:29

Comments

Log in to comment.

No comments yet.