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/op/OPFaultGameFinder.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {
    IOptimismPortal,
    IDisputeGameFactory,
    IDisputeGame,
    IFaultDisputeGame
} from './OPInterfaces.sol';
import {OPFaultParams} from './OPStructs.sol';

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

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

error GameNotFound();

struct Config {
    uint256 finalityDelay;
    uint256 respectedGameType;
}

contract OPFaultGameFinder {
    function findGameIndex(
        OPFaultParams memory params,
        uint256 gameCount
    ) external view virtual returns (uint256) {
        Config memory config = _config(params.portal);
        IDisputeGameFactory factory = params.portal.disputeGameFactory();
        if (gameCount == 0) gameCount = factory.gameCount();
        while (gameCount > 0) {
            (
                uint256 gameType,
                uint256 created,
                IDisputeGame gameProxy
            ) = factory.gameAtIndex(--gameCount);
            if (_isGameUsable(gameProxy, gameType, created, params, config)) {
                return gameCount;
            }
        }
        revert GameNotFound();
    }

    function gameAtIndex(
        OPFaultParams memory params,
        uint256 gameIndex
    )
        external
        view
        returns (
            uint256 gameType,
            uint256 created,
            IDisputeGame gameProxy,
            uint256 l2BlockNumber,
            bytes32 rootClaim
        )
    {
        IDisputeGameFactory factory = params.portal.disputeGameFactory();
        (gameType, created, gameProxy) = factory.gameAtIndex(gameIndex);
        if (
            _isGameUsable(
                gameProxy,
                gameType,
                created,
                params,
                _config(params.portal)
            )
        ) {
            l2BlockNumber = gameProxy.l2BlockNumber();
            rootClaim = gameProxy.rootClaim();
        }
    }

    function _isGameUsable(
        IDisputeGame gameProxy,
        uint256 gameType,
        uint256 created,
        OPFaultParams memory params,
        Config memory config
    ) internal view returns (bool) {
        // if allowed gameTypes is empty, accept a respected game OR a previously respected game
        if (
            params.allowedGameTypes.length == 0
                ? (gameType != config.respectedGameType &&
                    !gameProxy.wasRespectedGameTypeWhenCreated())
                : !_isAllowedGameType(gameType, params.allowedGameTypes)
        ) {
            return false;
        }
        if (
            !_isAllowedProposer(
                gameProxy.gameCreator(),
                params.allowedProposers
            )
        ) return false;
        // https://specs.optimism.io/fault-proof/stage-one/bridge-integration.html#blacklisting-disputegames
        if (params.portal.disputeGameBlacklist(gameProxy)) return false;
        if (params.minAgeSec > 0) {
            if (created > block.timestamp - params.minAgeSec) return false;
            (bool ok, bytes memory v) = address(gameProxy).staticcall(
                abi.encodeCall(IFaultDisputeGame.l2BlockNumberChallenged, ())
            );
            if (ok && bytes32(v) != bytes32(0)) {
				return false; // block number was challenged
            }
            if (gameProxy.status() != CHALLENGER_WINS) {
                return true; // not successfully challenged
            }
        }
        // require resolved + sufficiently aged
        return
            gameProxy.status() == DEFENDER_WINS &&
            (block.timestamp - gameProxy.resolvedAt()) >= config.finalityDelay;
    }

    function _isAllowedGameType(
        uint256 gameType,
        uint256[] memory allowedGameTypes
    ) internal pure returns (bool) {
        for (uint256 i; i < allowedGameTypes.length; ++i) {
            if (allowedGameTypes[i] == gameType) return true;
        }
        return false;
    }

    function _isAllowedProposer(
        address proposer,
        address[] memory allowedProposers
    ) internal pure returns (bool) {
        for (uint256 i; i < allowedProposers.length; ++i) {
            if (allowedProposers[i] == proposer) return true;
        }
        return allowedProposers.length == 0;
    }

    function _config(
        IOptimismPortal portal
    ) internal view returns (Config memory) {
        return
            Config({
                finalityDelay: portal.disputeGameFinalityDelaySeconds(),
                respectedGameType: portal.respectedGameType()
            });
    }
}
"
    },
    "contracts/op/OPInterfaces.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { OPFaultParams } from './OPStructs.sol';

// https://github.com/ethereum-optimism/optimism/blob/v1.13.7/packages/contracts-bedrock/src/L1/OptimismPortal2.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/v1.13.7/packages/contracts-bedrock/interfaces/dispute/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/v1.13.7/packages/contracts-bedrock/interfaces/dispute/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);
    function wasRespectedGameTypeWhenCreated() external view returns (bool);
    function gameCreator() external view returns (address);
    function createdAt() external view returns (uint64);
}

interface IOPFaultGameFinder {
    function findGameIndex(
        OPFaultParams memory params,
        uint256 gameCount
    ) external view returns (uint256);
    function gameAtIndex(
        OPFaultParams memory params,
        uint256 gameIndex
    )
        external
        view
        returns (
            uint256 gameType,
            uint256 created,
            IDisputeGame gameProxy,
            uint256 l2BlockNumber,
            bytes32 rootClaim
        );
}

// https://github.com/ethereum-optimism/optimism/blob/v1.13.7/packages/contracts-bedrock/interfaces/dispute/IFaultDisputeGame.sol
interface IFaultDisputeGame {
    function l2BlockNumberChallenged() external view returns (bool);
}"
    },
    "contracts/op/OPStructs.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IOptimismPortal } from './OPInterfaces.sol';

struct OPFaultParams {
    IOptimismPortal portal;
    uint256 minAgeSec;
    uint256[] allowedGameTypes;
    address[] allowedProposers;
}
"
    }
  },
  "settings": {
    "remappings": [
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "@eth-optimism/=lib/optimism/packages/",
      "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "forge-std/=lib/forge-std/src/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "optimism/=lib/optimism/packages/contracts-bedrock/src/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 1
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": false
  }
}}

Tags:
Factory|addr:0x4e63bfa938fa1235a0ad7aafa5165c6511f970ec|verified:true|block:23737788|tx:0x52a6bbf32eb7d252ed8646e931e5438215ff9a935952bde9622b66a382f0ec61|first_check:1762426015

Submitted on: 2025-11-06 11:46:56

Comments

Log in to comment.

No comments yet.