Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
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;
import { IOptimismPortal, IDisputeGameFactory, IDisputeGame, IFaultDisputeGame } from './OPInterfaces.sol';
import { OPFaultParams, FinalizationParams } from './OPStructs.sol';
import { GameStatus, GameTypes } from '@optimism/packages/contracts-bedrock/src/dispute/lib/Types.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;
// https://github.com/ethereum-optimism/optimism/blob/v1.13.7/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(
OPFaultParams memory params,
uint256 gameCount
) external view virtual returns (uint256) {
FinalizationParams memory finalizationParams = FinalizationParams({
finalityDelay: params.portal.disputeGameFinalityDelaySeconds(),
gameTypeUpdatedAt: params.portal.respectedGameTypeUpdatedAt()
});
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,
finalizationParams
)
) {
return gameCount;
}
}
revert GameNotFound();
}
function gameAtIndex(
OPFaultParams memory params,
uint256 gameIndex
)
external
view
returns (
uint256 gameType,
uint256 created,
IDisputeGame gameProxy,
uint256 l2BlockNumber,
bytes32 rootClaim
)
{
FinalizationParams memory finalizationParams = FinalizationParams({
finalityDelay: params.portal.disputeGameFinalityDelaySeconds(),
gameTypeUpdatedAt: params.portal.respectedGameTypeUpdatedAt()
});
IDisputeGameFactory factory = params.portal.disputeGameFactory();
(gameType, created, gameProxy) = factory.gameAtIndex(gameIndex);
if (
_isGameUsable(
gameProxy,
gameType,
created,
params,
finalizationParams
)
) {
l2BlockNumber = gameProxy.l2BlockNumber();
rootClaim = gameProxy.rootClaim();
}
}
function _isGameUsable(
IDisputeGame gameProxy,
uint256 gameType,
uint256 created,
OPFaultParams memory params,
FinalizationParams memory finalizationParams
) internal view returns (bool) {
if (!_isAllowedGameType(gameType, params.allowedGameTypes)) return false;
// https://specs.optimism.io/fault-proof/stage-one/bridge-integration.html#blacklisting-disputegames
if (params.portal.disputeGameBlacklist(gameProxy)) return false;
if (!gameProxy.wasRespectedGameTypeWhenCreated()) return false;
if (params.minAgeSec > 0) {
if (created > block.timestamp - params.minAgeSec) return false;
if (
gameType == GAME_TYPE_CANNON ||
gameType == GAME_TYPE_PERMISSIONED_CANNON
) {
return IFaultDisputeGame(address(gameProxy))
.l2BlockNumberChallenged() ? false : 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;
}
function _isAllowedProposer(address proposer, address[] memory allowedProposers) pure internal returns (bool) {
if (allowedProposers.length == 0) return true;
for (uint i = 0; i < allowedProposers.length; i++) {
if (allowedProposers[i] == proposer) return true;
}
return false;
}
}
"
},
"contracts/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/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;
}
struct FinalizationParams {
uint256 finalityDelay;
uint64 gameTypeUpdatedAt;
}"
},
"lib/optimism/packages/contracts-bedrock/src/dispute/lib/Types.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Libraries
import {
Position,
Hash,
GameType,
VMStatus,
Timestamp,
Duration,
Clock,
GameId,
Claim,
LibGameId,
LibClock
} from "./LibUDT.sol";
/// @notice The current status of the dispute game.
enum GameStatus {
// The game is currently in progress, and has not been resolved.
IN_PROGRESS,
// The game has concluded, and the `rootClaim` was challenged successfully.
CHALLENGER_WINS,
// The game has concluded, and the `rootClaim` could not be contested.
DEFENDER_WINS
}
/// @notice The game's bond distribution type. Games are expected to start in the `UNDECIDED`
/// state, and then choose either `NORMAL` or `REFUND`.
enum BondDistributionMode {
// Bond distribution strategy has not been chosen.
UNDECIDED,
// Bonds should be distributed as normal.
NORMAL,
// Bonds should be refunded to claimants.
REFUND
}
/// @notice Represents an L2 root and the L2 sequence number at which it was generated.
/// @custom:field root The output root.
/// @custom:field l2SequenceNumber The L2 Sequence Number ( e.g. block number / timestamp) at which the root was
/// generated.
struct Proposal {
Hash root;
uint256 l2SequenceNumber;
}
/// @title GameTypes
/// @notice A library that defines the IDs of games that can be played.
library GameTypes {
/// @dev A dispute game type the uses the cannon vm.
GameType internal constant CANNON = GameType.wrap(0);
/// @dev A permissioned dispute game type that uses the cannon vm.
GameType internal constant PERMISSIONED_CANNON = GameType.wrap(1);
/// @notice A dispute game type that uses the asterisc vm.
GameType internal constant ASTERISC = GameType.wrap(2);
/// @notice A dispute game type that uses the asterisc vm with Kona.
GameType internal constant ASTERISC_KONA = GameType.wrap(3);
/// @notice A dispute game type that uses the cannon vm (Super Roots).
GameType internal constant SUPER_CANNON = GameType.wrap(4);
/// @notice A dispute game type that uses the permissioned cannon vm (Super Roots).
GameType internal constant SUPER_PERMISSIONED_CANNON = GameType.wrap(5);
/// @notice A dispute game type that uses OP Succinct
GameType internal constant OP_SUCCINCT = GameType.wrap(6);
/// @notice A dispute game type that uses the asterisc vm with Kona (Super Roots).
GameType internal constant SUPER_ASTERISC_KONA = GameType.wrap(7);
/// @notice A dispute game type that uses the cannon vm with Kona.
GameType internal constant CANNON_KONA = GameType.wrap(8);
/// @notice A dispute game type that uses the cannon vm with Kona (Super Roots).
GameType internal constant SUPER_CANNON_KONA = GameType.wrap(9);
/// @notice A dispute game type with short game duration for testing withdrawals.
/// Not intended for production use.
GameType internal constant FAST = GameType.wrap(254);
/// @notice A dispute game type that uses an alphabet vm.
/// Not intended for production use.
GameType internal constant ALPHABET = GameType.wrap(255);
/// @notice A dispute game type that uses RISC Zero's Kailua
GameType internal constant KAILUA = GameType.wrap(1337);
}
/// @title VMStatuses
/// @notice Named type aliases for the various valid VM status bytes.
library VMStatuses {
/// @notice The VM has executed successfully and the outcome is valid.
VMStatus internal constant VALID = VMStatus.wrap(0);
/// @notice The VM has executed successfully and the outcome is invalid.
VMStatus internal constant INVALID = VMStatus.wrap(1);
/// @notice The VM has paniced.
VMStatus internal constant PANIC = VMStatus.wrap(2);
/// @notice The VM execution is still in progress.
VMStatus internal constant UNFINISHED = VMStatus.wrap(3);
}
/// @title LocalPreimageKey
/// @notice Named type aliases for local `PreimageOracle` key identifiers.
library LocalPreimageKey {
/// @notice The identifier for the L1 head hash.
uint256 internal constant L1_HEAD_HASH = 0x01;
/// @notice The identifier for the starting output root.
uint256 internal constant STARTING_OUTPUT_ROOT = 0x02;
/// @notice The identifier for the disputed output root.
uint256 internal constant DISPUTED_OUTPUT_ROOT = 0x03;
/// @notice The identifier for the disputed L2 block number.
uint256 internal constant DISPUTED_L2_BLOCK_NUMBER = 0x04;
/// @notice The identifier for the chain ID.
uint256 internal constant CHAIN_ID = 0x05;
}
"
},
"lib/optimism/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Libraries
import { Position } from "./LibPosition.sol";
using LibClaim for Claim global;
using LibHash for Hash global;
using LibDuration for Duration global;
using LibClock for Clock global;
using LibGameId for GameId global;
using LibTimestamp for Timestamp global;
using LibVMStatus for VMStatus global;
using LibGameType for GameType global;
/// @notice A `Clock` represents a packed `Duration` and `Timestamp`
/// @dev The packed layout of this type is as follows:
/// ┌────────────┬────────────────┐
/// │ Bits │ Value │
/// ├────────────┼────────────────┤
/// │ [0, 64) │ Duration │
/// │ [64, 128) │ Timestamp │
/// └────────────┴────────────────┘
type Clock is uint128;
/// @title LibClock
/// @notice This library contains helper functions for working with the `Clock` type.
library LibClock {
/// @notice Packs a `Duration` and `Timestamp` into a `Clock` type.
/// @param _duration The `Duration` to pack into the `Clock` type.
/// @param _timestamp The `Timestamp` to pack into the `Clock` type.
/// @return clock_ The `Clock` containing the `_duration` and `_timestamp`.
function wrap(Duration _duration, Timestamp _timestamp) internal pure returns (Clock clock_) {
assembly {
clock_ := or(shl(0x40, _duration), _timestamp)
}
}
/// @notice Pull the `Duration` out of a `Clock` type.
/// @param _clock The `Clock` type to pull the `Duration` out of.
/// @return duration_ The `Duration` pulled out of `_clock`.
function duration(Clock _clock) internal pure returns (Duration duration_) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `duration`.
assembly {
duration_ := shr(0x40, _clock)
}
}
/// @notice Pull the `Timestamp` out of a `Clock` type.
/// @param _clock The `Clock` type to pull the `Timestamp` out of.
/// @return timestamp_ The `Timestamp` pulled out of `_clock`.
function timestamp(Clock _clock) internal pure returns (Timestamp timestamp_) {
// Clean the high-order 192 bits by shifting the clock left and then right again, leaving
// only the `timestamp`.
assembly {
timestamp_ := shr(0xC0, shl(0xC0, _clock))
}
}
/// @notice Get the value of a `Clock` type in the form of the underlying uint128.
/// @param _clock The `Clock` type to get the value of.
/// @return clock_ The value of the `Clock` type as a uint128 type.
function raw(Clock _clock) internal pure returns (uint128 clock_) {
assembly {
clock_ := _clock
}
}
}
/// @notice A `GameId` represents a packed 4 byte game ID, a 8 byte timestamp, and a 20 byte address.
/// @dev The packed layout of this type is as follows:
/// ┌───────────┬───────────┐
/// │ Bits │ Value │
/// ├───────────┼───────────┤
/// │ [0, 32) │ Game Type │
/// │ [32, 96) │ Timestamp │
/// │ [96, 256) │ Address │
/// └───────────┴───────────┘
type GameId is bytes32;
/// @title LibGameId
/// @notice Utility functions for packing and unpacking GameIds.
library LibGameId {
/// @notice Packs values into a 32 byte GameId type.
/// @param _gameType The game type.
/// @param _timestamp The timestamp of the game's creation.
/// @param _gameProxy The game proxy address.
/// @return gameId_ The packed GameId.
function pack(
GameType _gameType,
Timestamp _timestamp,
address _gameProxy
)
internal
pure
returns (GameId gameId_)
{
assembly {
gameId_ := or(or(shl(224, _gameType), shl(160, _timestamp)), _gameProxy)
}
}
/// @notice Unpacks values from a 32 byte GameId type.
/// @param _gameId The packed GameId.
/// @return gameType_ The game type.
/// @return timestamp_ The timestamp of the game's creation.
/// @return gameProxy_ The game proxy address.
function unpack(GameId _gameId)
internal
pure
returns (GameType gameType_, Timestamp timestamp_, address gameProxy_)
{
assembly {
gameType_ := shr(224, _gameId)
timestamp_ := and(shr(160, _gameId), 0xFFFFFFFFFFFFFFFF)
gameProxy_ := and(_gameId, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
}
}
}
/// @notice A claim represents an MPT root representing the state of the fault proof program.
type Claim is bytes32;
/// @title LibClaim
/// @notice This library contains helper functions for working with the `Claim` type.
library LibClaim {
/// @notice Get the value of a `Claim` type in the form of the underlying bytes32.
/// @param _claim The `Claim` type to get the value of.
/// @return claim_ The value of the `Claim` type as a bytes32 type.
function raw(Claim _claim) internal pure returns (bytes32 claim_) {
assembly {
claim_ := _claim
}
}
/// @notice Hashes a claim and a position together.
/// @param _claim A Claim type.
/// @param _position The position of `claim`.
/// @param _challengeIndex The index of the claim being moved against.
/// @return claimHash_ A hash of abi.encodePacked(claim, position|challengeIndex);
function hashClaimPos(
Claim _claim,
Position _position,
uint256 _challengeIndex
)
internal
pure
returns (Hash claimHash_)
{
assembly {
mstore(0x00, _claim)
mstore(0x20, or(shl(128, _position), and(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, _challengeIndex)))
claimHash_ := keccak256(0x00, 0x40)
}
}
}
/// @notice A dedicated duration type.
/// @dev Unit: seconds
type Duration is uint64;
/// @title LibDuration
/// @notice This library contains helper functions for working with the `Duration` type.
library LibDuration {
/// @notice Get the value of a `Duration` type in the form of the underlying uint64.
/// @param _duration The `Duration` type to get the value of.
/// @return duration_ The value of the `Duration` type as a uint64 type.
function raw(Duration _duration) internal pure returns (uint64 duration_) {
assembly {
duration_ := _duration
}
}
}
/// @notice A custom type for a generic hash.
type Hash is bytes32;
/// @title LibHash
/// @notice This library contains helper functions for working with the `Hash` type.
library LibHash {
/// @notice Get the value of a `Hash` type in the form of the underlying bytes32.
/// @param _hash The `Hash` type to get the value of.
/// @return hash_ The value of the `Hash` type as a bytes32 type.
function raw(Hash _hash) internal pure returns (bytes32 hash_) {
assembly {
hash_ := _hash
}
}
}
/// @notice A dedicated timestamp type.
type Timestamp is uint64;
/// @title LibTimestamp
/// @notice This library contains helper functions for working with the `Timestamp` type.
library LibTimestamp {
/// @notice Get the value of a `Timestamp` type in the form of the underlying uint64.
/// @param _timestamp The `Timestamp` type to get the value of.
/// @return timestamp_ The value of the `Timestamp` type as a uint64 type.
function raw(Timestamp _timestamp) internal pure returns (uint64 timestamp_) {
assembly {
timestamp_ := _timestamp
}
}
}
/// @notice A `VMStatus` represents the status of a VM execution.
type VMStatus is uint8;
/// @title LibVMStatus
/// @notice This library contains helper functions for working with the `VMStatus` type.
library LibVMStatus {
/// @notice Get the value of a `VMStatus` type in the form of the underlying uint8.
/// @param _vmstatus The `VMStatus` type to get the value of.
/// @return vmstatus_ The value of the `VMStatus` type as a uint8 type.
function raw(VMStatus _vmstatus) internal pure returns (uint8 vmstatus_) {
assembly {
vmstatus_ := _vmstatus
}
}
}
/// @notice A `GameType` represents the type of game being played.
type GameType is uint32;
/// @title LibGameType
/// @notice This library contains helper functions for working with the `GameType` type.
library LibGameType {
/// @notice Get the value of a `GameType` type in the form of the underlying uint32.
/// @param _gametype The `GameType` type to get the value of.
/// @return gametype_ The value of the `GameType` type as a uint32 type.
function raw(GameType _gametype) internal pure returns (uint32 gametype_) {
assembly {
gametype_ := _gametype
}
}
}
"
},
"lib/optimism/packages/contracts-bedrock/src/dispute/lib/LibPosition.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
using LibPosition for Position global;
/// @notice A `Position` represents a position of a claim within the game tree.
/// @dev This is represented as a "generalized index" where the high-order bit
/// is the level in the tree and the remaining bits is a unique bit pattern, allowing
/// a unique identifier for each node in the tree. Mathematically, it is calculated
/// as 2^{depth} + indexAtDepth.
type Position is uint128;
/// @title LibPosition
/// @notice This library contains helper functions for working with the `Position` type.
library LibPosition {
/// @notice the `MAX_POSITION_BITLEN` is the number of bits that the `Position` type, and the implementation of
/// its behavior within this library, can safely support.
uint8 internal constant MAX_POSITION_BITLEN = 126;
/// @notice Computes a generalized index (2^{depth} + indexAtDepth).
/// @param _depth The depth of the position.
/// @param _indexAtDepth The index at the depth of the position.
/// @return position_ The computed generalized index.
function wrap(uint8 _depth, uint128 _indexAtDepth) internal pure returns (Position position_) {
assembly {
// gindex = 2^{_depth} + _indexAtDepth
position_ := add(shl(_depth, 1), _indexAtDepth)
}
}
/// @notice Pulls the `depth` out of a `Position` type.
/// @param _position The generalized index to get the `depth` of.
/// @return depth_ The `depth` of the `position` gindex.
/// @custom:attribution Solady <https://github.com/Vectorized/Solady>
function depth(Position _position) internal pure returns (uint8 depth_) {
// Return the most significant bit offset, which signifies the depth of the gindex.
assembly {
depth_ := or(depth_, shl(6, lt(0xffffffffffffffff, shr(depth_, _position))))
depth_ := or(depth_, shl(5, lt(0xffffffff, shr(depth_, _position))))
// For the remaining 32 bits, use a De Bruijn lookup.
_position := shr(depth_, _position)
_position := or(_position, shr(1, _position))
_position := or(_position, shr(2, _position))
_position := or(_position, shr(4, _position))
_position := or(_position, shr(8, _position))
_position := or(_position, shr(16, _position))
depth_ :=
or(
depth_,
byte(
shr(251, mul(_position, shl(224, 0x07c4acdd))),
0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f
)
)
}
}
/// @notice Pulls the `indexAtDepth` out of a `Position` type.
/// The `indexAtDepth` is the left/right index of a position at a specific depth within
/// the binary tree, starting from index 0. For example, at gindex 2, the `depth` = 1
/// and the `indexAtDepth` = 0.
/// @param _position The generalized index to get the `indexAtDepth` of.
/// @return indexAtDepth_ The `indexAtDepth` of the `position` gindex.
function indexAtDepth(Position _position) internal pure returns (uint128 indexAtDepth_) {
// Return bits p_{msb-1}...p_{0}. This effectively pulls the 2^{depth} out of the gindex,
// leaving only the `indexAtDepth`.
uint256 msb = depth(_position);
assembly {
indexAtDepth_ := sub(_position, shl(msb, 1))
}
}
/// @notice Get the left child of `_position`.
/// @param _position The position to get the left position of.
/// @return left_ The position to the left of `position`.
function left(Position _position) internal pure returns (Position left_) {
assembly {
left_ := shl(1, _position)
}
}
/// @notice Get the right child of `_position`
/// @param _position The position to get the right position of.
/// @return right_ The position to the right of `position`.
function right(Position _position) internal pure returns (Position right_) {
assembly {
right_ := or(1, shl(1, _position))
}
}
/// @notice Get the parent position of `_position`.
/// @param _position The position to get the parent position of.
/// @return parent_ The parent position of `position`.
function parent(Position _position) internal pure returns (Position parent_) {
assembly {
parent_ := shr(1, _position)
}
}
/// @notice Get the deepest, right most gindex relative to the `position`. This is equivalent to
/// calling `right` on a position until the maximum depth is reached.
/// @param _position The position to get the relative deepest, right most gindex of.
/// @param _maxDepth The maximum depth of the game.
/// @return rightIndex_ The deepest, right most gindex relative to the `position`.
function rightIndex(Position _position, uint256 _maxDepth) internal pure returns (Position rightIndex_) {
uint256 msb = depth(_position);
assembly {
let remaining := sub(_maxDepth, msb)
rightIndex_ := or(shl(remaining, _position), sub(shl(remaining, 1), 1))
}
}
/// @notice Get the deepest, right most trace index relative to the `position`. This is
/// equivalent to calling `right` on a position until the maximum depth is reached and
/// then finding its index at depth.
/// @param _position The position to get the relative trace index of.
/// @param _maxDepth The maximum depth of the game.
/// @return traceIndex_ The trace index relative to the `position`.
function traceIndex(Position _position, uint256 _maxDepth) internal pure returns (uint256 traceIndex_) {
uint256 msb = depth(_position);
assembly {
let remaining := sub(_maxDepth, msb)
traceIndex_ := sub(or(shl(remaining, _position), sub(shl(remaining, 1), 1)), shl(_maxDepth, 1))
}
}
/// @notice Gets the position of the highest ancestor of `_position` that commits to the same
/// trace index.
/// @param _position The position to get the highest ancestor of.
/// @return ancestor_ The highest ancestor of `position` that commits to the same trace index.
function traceAncestor(Position _position) internal pure returns (Position ancestor_) {
// Create a field with only the lowest unset bit of `_position` set.
Position lsb;
assembly {
lsb := and(not(_position), add(_position, 1))
}
// Find the index of the lowest unset bit within the field.
uint256 msb = depth(lsb);
// The highest ancestor that commits to the same trace index is the original position
// shifted right by the index of the lowest unset bit.
assembly {
let a := shr(msb, _position)
// Bound the ancestor to the minimum gindex, 1.
ancestor_ := or(a, iszero(a))
}
}
/// @notice Gets the position of the highest ancestor of `_position` that commits to the same
/// trace index, while still being below `_upperBoundExclusive`.
/// @param _position The position to get the highest ancestor of.
/// @param _upperBoundExclusive The exclusive upper depth bound, used to inform where to stop in order
/// to not escape a sub-tree.
/// @return ancestor_ The highest ancestor of `position` that commits to the same trace index.
function traceAncestorBounded(
Position _position,
uint256 _upperBoundExclusive
)
internal
pure
returns (Position ancestor_)
{
// This function only works for positions that are below the upper bound.
if (_position.depth() <= _upperBoundExclusive) {
assembly {
// Revert with `ClaimAboveSplit()`
mstore(0x00, 0xb34b5c22)
revert(0x1C, 0x04)
}
}
// Grab the global trace ancestor.
ancestor_ = traceAncestor(_position);
// If the ancestor is above or at the upper bound, shift it to be below the upper bound.
// This should be a special case that only covers positions that commit to the final leaf
// in a sub-tree.
if (ancestor_.depth() <= _upperBoundExclusive) {
ancestor_ = ancestor_.rightIndex(_upperBoundExclusive + 1);
}
}
/// @notice Get the move position of `_position`, which is the left child of:
/// 1. `_position` if `_isAttack` is true.
/// 2. `_position | 1` if `_isAttack` is false.
/// @param _position The position to get the relative attack/defense position of.
/// @param _isAttack Whether or not the move is an attack move.
/// @return move_ The move position relative to `position`.
function move(Position _position, bool _isAttack) internal pure returns (Position move_) {
assembly {
move_ := shl(1, or(iszero(_isAttack), _position))
}
}
/// @notice Get the value of a `Position` type in the form of the underlying uint128.
/// @param _position The position to get the value of.
/// @return raw_ The value of the `position` as a uint128 type.
function raw(Position _position) internal pure returns (uint128 raw_) {
assembly {
raw_ := _position
}
}
}
"
}
},
"settings": {
"remappings": [
"@unruggable/=lib/unruggable-gateways/",
"@optimism/=lib/optimism/",
"@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/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
}
}}
Submitted on: 2025-10-03 18:11:26
Comments
Log in to comment.
No comments yet.