Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x6104903d | 24125527 | 79 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0xb476cc5ecf2472a040dc381552b7a9bd7951a470
Contract Name:
OPSuccinctDisputeGame
Compiler Version
v0.8.15+commit.e14f2714
Optimization Enabled:
No with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {OPSuccinctL2OutputOracle} from "./OPSuccinctL2OutputOracle.sol";
import {Clone} from "@solady/utils/Clone.sol";
import {ISemver} from "interfaces/universal/ISemver.sol";
import {IDisputeGame} from "interfaces/dispute/IDisputeGame.sol";
import {Claim, GameStatus, GameType, GameTypes, Hash, Timestamp} from "@optimism/src/dispute/lib/Types.sol";
import {GameNotInProgress, OutOfOrderResolution} from "@optimism/src/dispute/lib/Errors.sol";
contract OPSuccinctDisputeGame is ISemver, Clone, IDisputeGame {
////////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////////
/// @notice The address of the L2 output oracle proxy contract.
address internal immutable L2_OUTPUT_ORACLE;
/// @notice The timestamp of the game's global creation.
Timestamp public createdAt;
/// @notice The timestamp of the game's global resolution.
Timestamp public resolvedAt;
/// @notice Returns the current status of the game.
GameStatus public status;
/// @notice A boolean for whether or not the game type was respected when the game was created.
bool public wasRespectedGameTypeWhenCreated;
/// @notice Semantic version.
/// @custom:semver v3.0.0
string public constant version = "v3.0.0";
constructor(address _l2OutputOracle) {
L2_OUTPUT_ORACLE = _l2OutputOracle;
}
////////////////////////////////////////////////////////////
// IDisputeGame impl //
////////////////////////////////////////////////////////////
function initialize() external payable {
createdAt = Timestamp.wrap(uint64(block.timestamp));
status = GameStatus.IN_PROGRESS;
wasRespectedGameTypeWhenCreated = true;
OPSuccinctL2OutputOracle oracle = OPSuccinctL2OutputOracle(L2_OUTPUT_ORACLE);
oracle.proposeL2Output(
configName(), rootClaim().raw(), l2BlockNumber(), l1BlockNumber(), proof(), proverAddress()
);
this.resolve();
}
/// @notice Getter for the game type.
/// @dev The reference impl should be entirely different depending on the type (fault, validity)
/// i.e. The game type should indicate the security model.
/// @return gameType_ The type of proof system being used.
function gameType() public pure returns (GameType) {
return GameTypes.OP_SUCCINCT;
}
/// @notice Getter for the creator of the dispute game.
/// @dev `clones-with-immutable-args` argument #1
/// Note: for validly created OPSuccinctDisputeGames, the creator is always the L2_OUTPUT_ORACLE.
/// @return The creator of the dispute game.
function gameCreator() public pure returns (address) {
return _getArgAddress(0x00);
}
/// @notice Getter for the root claim.
/// @dev `clones-with-immutable-args` argument #2
/// @return The root claim of the DisputeGame.
function rootClaim() public pure returns (Claim) {
return Claim.wrap(_getArgBytes32(0x14));
}
/// @notice Getter for the parent hash of the L1 block when the dispute game was created.
/// @dev `clones-with-immutable-args` argument #3
/// @return The parent hash of the L1 block when the dispute game was created.
function l1Head() public pure returns (Hash) {
return Hash.wrap(_getArgBytes32(0x34));
}
/// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`.
function l2BlockNumber() public pure returns (uint256 l2BlockNumber_) {
l2BlockNumber_ = _getArgUint256(0x54);
}
/// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`.
function l1BlockNumber() public pure returns (uint256 l1BlockNumber_) {
l1BlockNumber_ = _getArgUint256(0x74);
}
/// @notice The prover address of the disputed output root in the `L2OutputOracle`.
function proverAddress() public pure returns (address proverAddress_) {
proverAddress_ = _getArgAddress(0x94);
}
/// @notice Getter for the config name.
/// @return configName_ The config name to use for the L2OutputOracle.
function configName() public pure returns (bytes32 configName_) {
configName_ = _getArgBytes32(0xA8);
}
/// @notice The SP1 proof of the new output root. To be verified in the `L2OutputOracle`.
function proof() public pure returns (bytes memory proof_) {
uint256 len;
assembly {
// 0xC8 is the starting point of the proof in the calldata.
// calldataload(sub(calldatasize(), 2)) loads the last 2 bytes of the calldata, which gives the length of the immutable args.
// shr(240, calldataload(sub(calldatasize(), 2))) masks the last 30 bytes loaded in the previous step, so only the length of the immutable args is left.
// sub(sub(...)) subtracts the length of the immutable args (2 bytes) and the starting point of the proof (0xC8).
len := sub(sub(shr(240, calldataload(sub(calldatasize(), 2))), 2), 0xC8)
}
proof_ = _getArgBytes(0xC8, len);
}
/// @notice Getter for the extra data.
/// @dev `clones-with-immutable-args` argument #4
/// @return extraData_ Any extra data supplied to the dispute game contract by the creator.
function extraData() public pure returns (bytes memory extraData_) {
uint256 len;
assembly {
// 0x54 is the starting point of the extra data in the calldata.
// calldataload(sub(calldatasize(), 2)) loads the last 2 bytes of the calldata, which gives the length of the immutable args.
// shr(240, calldataload(sub(calldatasize(), 2))) masks the last 30 bytes loaded in the previous step, so only the length of the immutable args is left.
// sub(sub(...)) subtracts the length of the immutable args (2 bytes) and the starting point of the extra data (0x54).
len := sub(sub(shr(240, calldataload(sub(calldatasize(), 2))), 2), 0x54)
}
extraData_ = _getArgBytes(0x54, len);
}
/// @notice If all necessary information has been gathered, this function should mark the game
/// status as either `CHALLENGER_WINS` or `DEFENDER_WINS` and return the status of
/// the resolved game. It is at this stage that the bonds should be awarded to the
/// necessary parties.
/// @dev May only be called if the `status` is `IN_PROGRESS`.
/// @return status_ The status of the game after resolution.
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
resolvedAt = Timestamp.wrap(uint64(block.timestamp));
status_ = GameStatus.DEFENDER_WINS;
emit Resolved(status = status_);
}
/// @notice A compliant implementation of this interface should return the components of the
/// game UUID's preimage provided in the cwia payload. The preimage of the UUID is
/// constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes
/// concatenation.
/// @return gameType_ The type of proof system being used.
/// @return rootClaim_ The root claim of the DisputeGame.
/// @return extraData_ Any extra data supplied to the dispute game contract by the creator.
function gameData() external pure returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_) {
gameType_ = gameType();
rootClaim_ = rootClaim();
extraData_ = extraData();
}
////////////////////////////////////////////////////////////////
// IMMUTABLE GETTERS //
////////////////////////////////////////////////////////////////
/// @notice Getter for the L2OutputOracle.
/// @return l2OutputOracle_ The address of the L2OutputOracle.
function l2OutputOracle() external view returns (address l2OutputOracle_) {
l2OutputOracle_ = L2_OUTPUT_ORACLE;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {ISemver} from "interfaces/universal/ISemver.sol";
import {Types} from "@optimism/src/libraries/Types.sol";
import {AggregationOutputs} from "../lib/Types.sol";
import {Constants} from "@optimism/src/libraries/Constants.sol";
import {ISP1Verifier} from "@sp1-contracts/src/ISP1Verifier.sol";
import {GameType, GameTypes, Claim} from "@optimism/src/dispute/lib/Types.sol";
import {IDisputeGame} from "interfaces/dispute/IDisputeGame.sol";
import {IDisputeGameFactory} from "interfaces/dispute/IDisputeGameFactory.sol";
/// @custom:proxied
/// @title OPSuccinctL2OutputOracle
/// @notice The OPSuccinctL2OutputOracle contains an array of L2 state outputs, where each output is a
/// commitment to the state of the L2 chain. Other contracts like the OptimismPortal use
/// these outputs to verify information about the state of L2. The outputs posted to this contract
/// are proved to be valid with `op-succinct`.
contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @notice Configuration parameters for OP Succinct verification.
struct OpSuccinctConfig {
/// @notice The verification key of the aggregation SP1 program.
bytes32 aggregationVkey;
/// @notice The 32 byte commitment to the BabyBear representation of the verification key of
/// the range SP1 program. Specifically, this verification key is the output of converting
/// the [u32; 8] range BabyBear verification key to a [u8; 32] array.
bytes32 rangeVkeyCommitment;
/// @notice The hash of the chain's rollup config, which ensures the proofs submitted are for
/// the correct chain. This is used to prevent replay attacks.
bytes32 rollupConfigHash;
}
/// @notice Parameters to initialize the OPSuccinctL2OutputOracle contract.
struct InitParams {
address challenger;
address proposer;
address owner;
uint256 finalizationPeriodSeconds;
uint256 l2BlockTime;
bytes32 aggregationVkey;
bytes32 rangeVkeyCommitment;
bytes32 rollupConfigHash;
bytes32 startingOutputRoot;
uint256 startingBlockNumber;
uint256 startingTimestamp;
uint256 submissionInterval;
address verifier;
uint256 fallbackTimeout;
}
/// @notice The number of the first L2 block recorded in this contract.
uint256 public startingBlockNumber;
/// @notice The timestamp of the first L2 block recorded in this contract.
uint256 public startingTimestamp;
/// @notice An array of L2 output proposals.
Types.OutputProposal[] internal l2Outputs;
/// @notice The minimum interval in L2 blocks at which checkpoints must be submitted.
/// @custom:network-specific
uint256 public submissionInterval;
/// @notice The time between L2 blocks in seconds. Once set, this value MUST NOT be modified.
/// @custom:network-specific
uint256 public l2BlockTime;
/// @notice The address of the challenger. Can be updated via upgrade.
/// @custom:network-specific
address public challenger;
/// @notice The address of the proposer. Can be updated via upgrade. DEPRECATED: Use approvedProposers mapping instead.
/// @custom:network-specific
/// @custom:deprecated
address public proposer;
/// @notice The minimum time (in seconds) that must elapse before a withdrawal can be finalized.
/// @custom:network-specific
uint256 public finalizationPeriodSeconds;
/// @notice The verification key of the aggregation SP1 program.
/// @custom:deprecated
bytes32 public aggregationVkey;
/// @notice The 32 byte commitment to the BabyBear representation of the verification key of the range SP1 program. Specifically,
/// this verification is the output of converting the [u32; 8] range BabyBear verification key to a [u8; 32] array.
/// @custom:deprecated
bytes32 public rangeVkeyCommitment;
/// @notice The deployed SP1Verifier contract to verify proofs.
address public verifier;
/// @notice The hash of the chain's rollup config, which ensures the proofs submitted are for the correct chain.
/// @custom:deprecated
bytes32 public rollupConfigHash;
/// @notice The owner of the contract, who has admin permissions.
address public owner;
/// @notice The proposers that can propose new proofs.
mapping(address => bool) public approvedProposers;
/// @notice A trusted mapping of block numbers to block hashes.
mapping(uint256 => bytes32) public historicBlockHashes;
/// @notice Activate optimistic mode. When true, the contract will accept outputs without verification.
bool public optimisticMode;
/// @notice The time threshold (in seconds) after which anyone can submit a proposal if no proposal has been submitted.
/// Only applies in permissioned mode.
/// @custom:network-specific
uint256 public fallbackTimeout;
/// @notice Mapping of configuration names to OpSuccinctConfig structs.
mapping(bytes32 => OpSuccinctConfig) public opSuccinctConfigs;
/// @notice The genesis configuration name.
bytes32 public constant GENESIS_CONFIG_NAME = keccak256("opsuccinct_genesis");
/// @notice The dispute game factory contract.
/// If running with the L2OutputOracle directly, without the OPSuccinctDisputeGame wrapper,
/// this is set to the zero address.
address public disputeGameFactory;
/// @notice Whether or not we are inside a call to DisputeGameFactory.create.
bool internal _enteredDGFCreate;
////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
/// @notice Emitted when an output is proposed.
/// @param outputRoot The output root.
/// @param l2OutputIndex The index of the output in the l2Outputs array.
/// @param l2BlockNumber The L2 block number of the output root.
/// @param l1Timestamp The L1 timestamp when proposed.
event OutputProposed(
bytes32 indexed outputRoot, uint256 indexed l2OutputIndex, uint256 indexed l2BlockNumber, uint256 l1Timestamp
);
/// @notice Emitted when outputs are deleted.
/// @param prevNextOutputIndex Next L2 output index before the deletion.
/// @param newNextOutputIndex Next L2 output index after the deletion.
event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);
/// @notice Emitted when an OP Succinct configuration is updated.
/// @param configName The name of the configuration.
/// @param aggregationVkey The aggregation verification key.
/// @param rangeVkeyCommitment The range verification key commitment.
/// @param rollupConfigHash The rollup config hash.
event OpSuccinctConfigUpdated(
bytes32 indexed configName, bytes32 aggregationVkey, bytes32 rangeVkeyCommitment, bytes32 rollupConfigHash
);
/// @notice Emitted when an OP Succinct configuration is deleted.
/// @param configName The name of the configuration that was deleted.
event OpSuccinctConfigDeleted(bytes32 indexed configName);
/// @notice Emitted when the verifier address is updated.
/// @param oldVerifier The old verifier address.
/// @param newVerifier The new verifier address.
event VerifierUpdated(address indexed oldVerifier, address indexed newVerifier);
/// @notice Emitted when the owner address is updated.
/// @param previousOwner The previous owner address.
/// @param newOwner The new owner address.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice Emitted when a proposer address is added.
/// @param proposer The proposer address.
/// @param added Whether the proposer was added or removed.
event ProposerUpdated(address indexed proposer, bool added);
/// @notice Emitted when the submission interval is updated.
/// @param oldSubmissionInterval The old submission interval.
/// @param newSubmissionInterval The new submission interval.
event SubmissionIntervalUpdated(uint256 oldSubmissionInterval, uint256 newSubmissionInterval);
/// @notice Emitted when the optimistic mode is toggled.
/// @param enabled Indicates whether optimistic mode is enabled or disabled.
/// @param finalizationPeriodSeconds The new finalization period in seconds.
event OptimisticModeToggled(bool indexed enabled, uint256 finalizationPeriodSeconds);
/// @notice Emitted when the dispute game factory address is set.
/// @param disputeGameFactory The dispute game factory address.
event DisputeGameFactorySet(address indexed disputeGameFactory);
////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
/// @notice The L1 block hash is not available. If the block hash requested is not in the last 256 blocks,
/// it is not available.
error L1BlockHashNotAvailable();
/// @notice The L1 block hash is not checkpointed.
error L1BlockHashNotCheckpointed();
/// @notice Semantic version.
/// @custom:semver v3.0.0
string public constant version = "v3.0.0";
/// @notice The version of the initializer on the contract. Used for managing upgrades.
uint8 public constant initializerVersion = 3;
////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////
modifier onlyOwner() {
require(msg.sender == owner, "L2OutputOracle: caller is not the owner");
_;
}
modifier whenOptimistic() {
require(optimisticMode, "L2OutputOracle: optimistic mode is not enabled");
_;
}
modifier whenNotOptimistic() {
require(!optimisticMode, "L2OutputOracle: optimistic mode is enabled");
_;
}
////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
/// @notice Constructs the OPSuccinctL2OutputOracle contract. Disables initializers.
constructor() {
_disableInitializers();
}
/// @notice Initializer.
/// @param _initParams The initialization parameters for the contract.
function initialize(InitParams memory _initParams) public reinitializer(initializerVersion) {
require(_initParams.submissionInterval > 0, "L2OutputOracle: submission interval must be greater than 0");
require(_initParams.l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0");
require(
_initParams.startingTimestamp <= block.timestamp,
"L2OutputOracle: starting L2 timestamp must be less than current time"
);
submissionInterval = _initParams.submissionInterval;
l2BlockTime = _initParams.l2BlockTime;
// For proof verification to work, there must be an initial output.
// Disregard the _startingBlockNumber and _startingTimestamp parameters during upgrades, as they're already set.
if (l2Outputs.length == 0) {
l2Outputs.push(
Types.OutputProposal({
outputRoot: _initParams.startingOutputRoot,
timestamp: uint128(_initParams.startingTimestamp),
l2BlockNumber: uint128(_initParams.startingBlockNumber)
})
);
startingBlockNumber = _initParams.startingBlockNumber;
startingTimestamp = _initParams.startingTimestamp;
}
challenger = _initParams.challenger;
finalizationPeriodSeconds = _initParams.finalizationPeriodSeconds;
// Add the initial proposer.
approvedProposers[_initParams.proposer] = true;
// Initialize the permissionless fallback timeout.
fallbackTimeout = _initParams.fallbackTimeout;
// Initialize genesis configuration
opSuccinctConfigs[GENESIS_CONFIG_NAME] = OpSuccinctConfig({
aggregationVkey: _initParams.aggregationVkey,
rangeVkeyCommitment: _initParams.rangeVkeyCommitment,
rollupConfigHash: _initParams.rollupConfigHash
});
verifier = _initParams.verifier;
owner = _initParams.owner;
_enteredDGFCreate = false;
// This is set to the zero address by default.
disputeGameFactory = address(0);
}
/// @notice Deletes all output proposals after and including the proposal that corresponds to
/// the given output index. Only the challenger address can delete outputs.
/// @param _l2OutputIndex Index of the first L2 output to be deleted.
/// All outputs after this output will also be deleted.
function deleteL2Outputs(uint256 _l2OutputIndex) external {
require(msg.sender == challenger, "L2OutputOracle: only the challenger address can delete outputs");
// Make sure we're not *increasing* the length of the array.
require(
_l2OutputIndex < l2Outputs.length, "L2OutputOracle: cannot delete outputs after the latest output index"
);
// Do not allow deleting any outputs that have already been finalized.
require(
block.timestamp - l2Outputs[_l2OutputIndex].timestamp < finalizationPeriodSeconds,
"L2OutputOracle: cannot delete outputs that have already been finalized"
);
uint256 prevNextL2OutputIndex = nextOutputIndex();
// Use assembly to delete the array elements because Solidity doesn't allow it.
assembly {
sstore(l2Outputs.slot, _l2OutputIndex)
}
emit OutputsDeleted(prevNextL2OutputIndex, _l2OutputIndex);
}
/// @notice Accepts an outputRoot and the timestamp of the corresponding L2 block.
/// The timestamp must be equal to the current value returned by `nextTimestamp()` in
/// order to be accepted. This function may only be called by the Proposer.
/// @param _configName The name of the OP Succinct configuration to use.
/// @param _outputRoot The L2 output of the checkpoint block.
/// @param _l2BlockNumber The L2 block number that resulted in _outputRoot.
/// @param _l1BlockNumber The block number with the specified block hash.
/// @param _proof The aggregation proof that proves the transition from the latest L2 output to the new L2 output.
/// @param _proverAddress The address of the prover that submitted the proof. Note: proverAddress is not required to be the tx.origin as there is no reason to front-run the prover in the full validity setting.
/// @dev Modified the function signature to exclude the `_l1BlockHash` parameter, as it's redundant
/// for OP Succinct given the `_l1BlockNumber` parameter.
/// @dev Security Note: This contract uses `tx.origin` for proposer permission control due to usage of this contract
/// in the OPSuccinctDisputeGame, created via DisputeGameFactory using the Clone With Immutable Arguments (CWIA) pattern.
///
/// In this setup:
/// - `msg.sender` is the newly created game contract, not an approved proposer.
/// - `tx.origin` identifies the actual user initiating the transaction.
///
/// While `tx.origin` can be vulnerable in general, it is safe here because:
/// - Only trusted proposers/relayers call this contract.
/// - Proposers are expected to interact solely with trusted contracts.
///
/// As long as proposers avoid untrusted contracts, `tx.origin` is as secure as `msg.sender` in this context.
function proposeL2Output(
bytes32 _configName,
bytes32 _outputRoot,
uint256 _l2BlockNumber,
uint256 _l1BlockNumber,
bytes memory _proof,
address _proverAddress
) external whenNotOptimistic {
// The proposer must be explicitly approved, or the zero address must be approved (permissionless proposing),
// or the fallback timeout has been exceeded allowing anyone to propose.
require(
approvedProposers[tx.origin] || approvedProposers[address(0)]
|| (block.timestamp - lastProposalTimestamp() > fallbackTimeout),
"L2OutputOracle: only approved proposers can propose new outputs"
);
require(
_l2BlockNumber >= nextBlockNumber(),
"L2OutputOracle: block number must be greater than or equal to next expected block number"
);
require(
computeL2Timestamp(_l2BlockNumber) < block.timestamp,
"L2OutputOracle: cannot propose L2 output in the future"
);
// If the dispute game factory is set, make sure that we are calling this function from within
// DisputeGameFactory.create.
if (disputeGameFactory != address(0)) {
require(
_enteredDGFCreate,
"L2OutputOracle: cannot propose L2 output from outside DisputeGameFactory.create while disputeGameFactory is set"
);
} else {
require(
!_enteredDGFCreate,
"L2OutputOracle: cannot propose L2 output from inside DisputeGameFactory.create without setting disputeGameFactory"
);
}
require(_outputRoot != bytes32(0), "L2OutputOracle: L2 output proposal cannot be the zero hash");
OpSuccinctConfig memory config = opSuccinctConfigs[_configName];
require(isValidOpSuccinctConfig(config), "L2OutputOracle: invalid OP Succinct configuration");
bytes32 l1BlockHash = historicBlockHashes[_l1BlockNumber];
if (l1BlockHash == bytes32(0)) {
revert L1BlockHashNotCheckpointed();
}
AggregationOutputs memory publicValues = AggregationOutputs({
l1Head: l1BlockHash,
l2PreRoot: l2Outputs[latestOutputIndex()].outputRoot,
claimRoot: _outputRoot,
claimBlockNum: _l2BlockNumber,
rollupConfigHash: config.rollupConfigHash,
rangeVkeyCommitment: config.rangeVkeyCommitment,
proverAddress: _proverAddress
});
ISP1Verifier(verifier).verifyProof(config.aggregationVkey, abi.encode(publicValues), _proof);
emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp);
l2Outputs.push(
Types.OutputProposal({
outputRoot: _outputRoot,
timestamp: uint128(block.timestamp),
l2BlockNumber: uint128(_l2BlockNumber)
})
);
}
/// @notice Accepts an outputRoot and the timestamp of the corresponding L2 block.
/// The timestamp must be equal to the current value returned by `nextTimestamp()` in
/// order to be accepted. This function may only be called by the Proposer.
/// @param _outputRoot The L2 output of the checkpoint block.
/// @param _l2BlockNumber The L2 block number that resulted in _outputRoot.
/// @param _l1BlockHash A block hash which must be included in the current chain.
/// @param _l1BlockNumber The block number with the specified block hash.
/// @dev This function is sourced from the original L2OutputOracle contract. The only modification is that the proposer address must be in the approvedProposers mapping, or permissionless proposing is enabled.
/// @dev This function is not compatible with the `OPSuccinctDisputeGame` contract as it uses `msg.sender` for proposer permission control.
/// See `whenNotOptimistic` implementation of `proposeL2Output` for more details.
/// If the functionality for optimistic mode is needed in the `OPSuccinctDisputeGame` contract, use mock mode instead.
function proposeL2Output(bytes32 _outputRoot, uint256 _l2BlockNumber, bytes32 _l1BlockHash, uint256 _l1BlockNumber)
external
payable
whenOptimistic
{
// The proposer must be explicitly approved, or the zero address must be approved (permissionless proposing).
// In optimistic mode, there is NO permissionless fallback timeout. That is, no matter how long it has been since the last proposal,
// non-permissioned proposers cannot propose in optimistic mode.
require(
approvedProposers[msg.sender] || approvedProposers[address(0)],
"L2OutputOracle: only approved proposers can propose new outputs"
);
require(
_l2BlockNumber == nextBlockNumber(),
"L2OutputOracle: block number must be equal to next expected block number"
);
require(
computeL2Timestamp(_l2BlockNumber) < block.timestamp,
"L2OutputOracle: cannot propose L2 output in the future"
);
require(_outputRoot != bytes32(0), "L2OutputOracle: L2 output proposal cannot be the zero hash");
if (_l1BlockHash != bytes32(0)) {
// This check allows the proposer to propose an output based on a given L1 block,
// without fear that it will be reorged out.
// It will also revert if the blockheight provided is more than 256 blocks behind the
// chain tip (as the hash will return as zero). This does open the door to a griefing
// attack in which the proposer's submission is censored until the block is no longer
// retrievable, if the proposer is experiencing this attack it can simply leave out the
// blockhash value, and delay submission until it is confident that the L1 block is
// finalized.
require(
blockhash(_l1BlockNumber) == _l1BlockHash,
"L2OutputOracle: block hash does not match the hash at the expected height"
);
}
emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp);
l2Outputs.push(
Types.OutputProposal({
outputRoot: _outputRoot,
timestamp: uint128(block.timestamp),
l2BlockNumber: uint128(_l2BlockNumber)
})
);
}
/// @notice Proposes an L2 output through the dispute game factory.
/// @dev We can only invoke `disputeGameFactory.create` through this function, ensuring that
/// no one can interact with `proposeL2Output` directly when the dispute game factory is set.
/// @param _configName The name of the OpSuccinctConfig to use.
/// @param _outputRoot The root of the L2 output to propose.
/// @param _l2BlockNumber The L2 block number of the output to propose.
/// @param _l1BlockNumber The L1 block number of the output to propose.
/// @param _proof The proof of the output to propose.
/// @param _proverAddress The address of the prover to use.
/// @return _game The dispute game created.
function dgfProposeL2Output(
bytes32 _configName,
bytes32 _outputRoot,
uint256 _l2BlockNumber,
uint256 _l1BlockNumber,
bytes memory _proof,
address _proverAddress
) external payable whenNotOptimistic returns (IDisputeGame _game) {
require(disputeGameFactory != address(0), "L2OutputOracle: dispute game factory is not set");
_enteredDGFCreate = true;
_game = IDisputeGameFactory(disputeGameFactory).create{value: msg.value}(
GameTypes.OP_SUCCINCT,
Claim.wrap(_outputRoot),
abi.encodePacked(_l2BlockNumber, _l1BlockNumber, _proverAddress, _configName, _proof)
);
_enteredDGFCreate = false;
}
/// @notice Checkpoints a block hash at a given block number.
/// @param _blockNumber Block number to checkpoint the hash at.
/// @dev If the block hash is not available, this will revert.
function checkpointBlockHash(uint256 _blockNumber) external {
bytes32 blockHash = blockhash(_blockNumber);
if (blockHash == bytes32(0)) {
revert L1BlockHashNotAvailable();
}
historicBlockHashes[_blockNumber] = blockHash;
}
/// @notice Returns an output by index. Needed to return a struct instead of a tuple.
/// @param _l2OutputIndex Index of the output to return.
/// @return The output at the given index.
function getL2Output(uint256 _l2OutputIndex) external view returns (Types.OutputProposal memory) {
return l2Outputs[_l2OutputIndex];
}
/// @notice Returns the index of the L2 output that checkpoints a given L2 block number.
/// Uses a binary search to find the first output greater than or equal to the given
/// block.
/// @param _l2BlockNumber L2 block number to find a checkpoint for.
/// @return Index of the first checkpoint that commits to the given L2 block number.
function getL2OutputIndexAfter(uint256 _l2BlockNumber) public view returns (uint256) {
// Make sure an output for this block number has actually been proposed.
require(
_l2BlockNumber <= latestBlockNumber(),
"L2OutputOracle: cannot get output for a block that has not been proposed"
);
// Make sure there's at least one output proposed.
require(l2Outputs.length > 0, "L2OutputOracle: cannot get output as no outputs have been proposed yet");
// Find the output via binary search, guaranteed to exist.
uint256 lo = 0;
uint256 hi = l2Outputs.length;
while (lo < hi) {
uint256 mid = (lo + hi) / 2;
if (l2Outputs[mid].l2BlockNumber < _l2BlockNumber) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
/// @notice Returns the L2 output proposal that checkpoints a given L2 block number.
/// Uses a binary search to find the first output greater than or equal to the given
/// block.
/// @param _l2BlockNumber L2 block number to find a checkpoint for.
/// @return First checkpoint that commits to the given L2 block number.
function getL2OutputAfter(uint256 _l2BlockNumber) external view returns (Types.OutputProposal memory) {
return l2Outputs[getL2OutputIndexAfter(_l2BlockNumber)];
}
/// @notice Returns the number of outputs that have been proposed.
/// Will revert if no outputs have been proposed yet.
/// @return The number of outputs that have been proposed.
function latestOutputIndex() public view returns (uint256) {
return l2Outputs.length - 1;
}
/// @notice Returns the index of the next output to be proposed.
/// @return The index of the next output to be proposed.
function nextOutputIndex() public view returns (uint256) {
return l2Outputs.length;
}
/// @notice Returns the block number of the latest submitted L2 output proposal.
/// If no proposals been submitted yet then this function will return the starting
/// block number.
/// @return Latest submitted L2 block number.
function latestBlockNumber() public view returns (uint256) {
return l2Outputs.length == 0 ? startingBlockNumber : l2Outputs[l2Outputs.length - 1].l2BlockNumber;
}
/// @notice Computes the block number of the next L2 block that needs to be checkpointed.
/// @return Next L2 block number.
function nextBlockNumber() public view returns (uint256) {
return latestBlockNumber() + submissionInterval;
}
/// @notice Returns the timestamp of the last submitted L2 output proposal.
/// If no proposals have been submitted yet, returns the starting timestamp.
/// @return Timestamp of the latest submitted L2 output proposal.
function lastProposalTimestamp() public view returns (uint256) {
return l2Outputs.length == 0 ? startingTimestamp : l2Outputs[l2Outputs.length - 1].timestamp;
}
/// @notice Returns the L2 timestamp corresponding to a given L2 block number.
/// @param _l2BlockNumber The L2 block number of the target block.
/// @return L2 timestamp of the given block.
function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
return startingTimestamp + ((_l2BlockNumber - startingBlockNumber) * l2BlockTime);
}
/// @notice Validates that an OpSuccinctConfig has all non-zero parameters.
/// @param _config The OpSuccinctConfig to validate.
/// @return True if all parameters are non-zero, false otherwise.
function isValidOpSuccinctConfig(OpSuccinctConfig memory _config) public pure returns (bool) {
return _config.aggregationVkey != bytes32(0) && _config.rangeVkeyCommitment != bytes32(0)
&& _config.rollupConfigHash != bytes32(0);
}
/// @notice Update the submission interval.
/// @param _submissionInterval The new submission interval.
function updateSubmissionInterval(uint256 _submissionInterval) external onlyOwner {
emit SubmissionIntervalUpdated(submissionInterval, _submissionInterval);
submissionInterval = _submissionInterval;
}
/// @notice Updates or creates an OP Succinct configuration.
/// @param _configName The name of the configuration.
/// @param _rollupConfigHash The rollup config hash.
/// @param _aggregationVkey The aggregation verification key.
/// @param _rangeVkeyCommitment The range verification key commitment.
function addOpSuccinctConfig(
bytes32 _configName,
bytes32 _rollupConfigHash,
bytes32 _aggregationVkey,
bytes32 _rangeVkeyCommitment
) external onlyOwner {
require(_configName != bytes32(0), "L2OutputOracle: config name cannot be empty");
require(!isValidOpSuccinctConfig(opSuccinctConfigs[_configName]), "L2OutputOracle: config already exists");
OpSuccinctConfig memory newConfig = OpSuccinctConfig({
aggregationVkey: _aggregationVkey,
rangeVkeyCommitment: _rangeVkeyCommitment,
rollupConfigHash: _rollupConfigHash
});
require(isValidOpSuccinctConfig(newConfig), "L2OutputOracle: invalid OP Succinct configuration parameters");
opSuccinctConfigs[_configName] = newConfig;
emit OpSuccinctConfigUpdated(_configName, _aggregationVkey, _rangeVkeyCommitment, _rollupConfigHash);
}
/// @notice Deletes an OP Succinct configuration.
/// @param _configName The name of the configuration to delete.
function deleteOpSuccinctConfig(bytes32 _configName) external onlyOwner {
delete opSuccinctConfigs[_configName];
emit OpSuccinctConfigDeleted(_configName);
}
/// @notice Updates the verifier address.
/// @param _verifier The new verifier address.
function updateVerifier(address _verifier) external onlyOwner {
emit VerifierUpdated(verifier, _verifier);
verifier = _verifier;
}
/// @notice Updates the owner address.
/// @param _owner The new owner address.
function transferOwnership(address _owner) external onlyOwner {
emit OwnershipTransferred(owner, _owner);
owner = _owner;
}
/// @notice Adds a new proposer address.
/// @param _proposer The new proposer address.
function addProposer(address _proposer) external onlyOwner {
approvedProposers[_proposer] = true;
emit ProposerUpdated(_proposer, true);
}
/// @notice Removes a proposer address.
/// @param _proposer The proposer address to remove.
function removeProposer(address _proposer) external onlyOwner {
approvedProposers[_proposer] = false;
emit ProposerUpdated(_proposer, false);
}
/// @notice Enables optimistic mode.
/// @param _finalizationPeriodSeconds The new finalization window.
function enableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenNotOptimistic {
finalizationPeriodSeconds = _finalizationPeriodSeconds;
optimisticMode = true;
emit OptimisticModeToggled(true, _finalizationPeriodSeconds);
}
/// @notice Sets the dispute game factory address.
/// @param _disputeGameFactory The dispute game factory address.
function setDisputeGameFactory(address _disputeGameFactory) external onlyOwner {
disputeGameFactory = _disputeGameFactory;
emit DisputeGameFactorySet(_disputeGameFactory);
}
/// @notice Disables optimistic mode.
/// @param _finalizationPeriodSeconds The new finalization window.
function disableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenOptimistic {
finalizationPeriodSeconds = _finalizationPeriodSeconds;
optimisticMode = false;
emit OptimisticModeToggled(false, _finalizationPeriodSeconds);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Class with helper read functions for clone with immutable args.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol)
/// @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
abstract contract Clone {
/// @dev Reads all of the immutable args.
function _getArgBytes() internal pure returns (bytes memory arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := mload(0x40)
let length := sub(calldatasize(), add(2, offset)) // 2 bytes are used for the length.
mstore(arg, length) // Store the length.
calldatacopy(add(arg, 0x20), offset, length)
let o := add(add(arg, 0x20), length)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/// @dev Reads an immutable arg with type bytes.
function _getArgBytes(uint256 argOffset, uint256 length)
internal
pure
returns (bytes memory arg)
{
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := mload(0x40)
mstore(arg, length) // Store the length.
calldatacopy(add(arg, 0x20), add(offset, argOffset), length)
let o := add(add(arg, 0x20), length)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/// @dev Reads an immutable arg with type address.
function _getArgAddress(uint256 argOffset) internal pure returns (address arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(96, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads a uint256 array stored in the immutable args.
function _getArgUint256Array(uint256 argOffset, uint256 length)
internal
pure
returns (uint256[] memory arg)
{
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := mload(0x40)
mstore(arg, length) // Store the length.
calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length))
mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory.
}
}
/// @dev Reads a bytes32 array stored in the immutable args.
function _getArgBytes32Array(uint256 argOffset, uint256 length)
internal
pure
returns (bytes32[] memory arg)
{
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := mload(0x40)
mstore(arg, length) // Store the length.
calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length))
mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory.
}
}
/// @dev Reads an immutable arg with type bytes32.
function _getArgBytes32(uint256 argOffset) internal pure returns (bytes32 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/// @dev Reads an immutable arg with type uint256.
function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/// @dev Reads an immutable arg with type uint248.
function _getArgUint248(uint256 argOffset) internal pure returns (uint248 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(8, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint240.
function _getArgUint240(uint256 argOffset) internal pure returns (uint240 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(16, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint232.
function _getArgUint232(uint256 argOffset) internal pure returns (uint232 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(24, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint224.
function _getArgUint224(uint256 argOffset) internal pure returns (uint224 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(0x20, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint216.
function _getArgUint216(uint256 argOffset) internal pure returns (uint216 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(40, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint208.
function _getArgUint208(uint256 argOffset) internal pure returns (uint208 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(48, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint200.
function _getArgUint200(uint256 argOffset) internal pure returns (uint200 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(56, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint192.
function _getArgUint192(uint256 argOffset) internal pure returns (uint192 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(64, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint184.
function _getArgUint184(uint256 argOffset) internal pure returns (uint184 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(72, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint176.
function _getArgUint176(uint256 argOffset) internal pure returns (uint176 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(80, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint168.
function _getArgUint168(uint256 argOffset) internal pure returns (uint168 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(88, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint160.
function _getArgUint160(uint256 argOffset) internal pure returns (uint160 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(96, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint152.
function _getArgUint152(uint256 argOffset) internal pure returns (uint152 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(104, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint144.
function _getArgUint144(uint256 argOffset) internal pure returns (uint144 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(112, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint136.
function _getArgUint136(uint256 argOffset) internal pure returns (uint136 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(120, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint128.
function _getArgUint128(uint256 argOffset) internal pure returns (uint128 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(128, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint120.
function _getArgUint120(uint256 argOffset) internal pure returns (uint120 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(136, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint112.
function _getArgUint112(uint256 argOffset) internal pure returns (uint112 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(144, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint104.
function _getArgUint104(uint256 argOffset) internal pure returns (uint104 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(152, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint96.
function _getArgUint96(uint256 argOffset) internal pure returns (uint96 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(160, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint88.
function _getArgUint88(uint256 argOffset) internal pure returns (uint88 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(168, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint80.
function _getArgUint80(uint256 argOffset) internal pure returns (uint80 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(176, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint72.
function _getArgUint72(uint256 argOffset) internal pure returns (uint72 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(184, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint64.
function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(192, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint56.
function _getArgUint56(uint256 argOffset) internal pure returns (uint56 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(200, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint48.
function _getArgUint48(uint256 argOffset) internal pure returns (uint48 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(208, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint40.
function _getArgUint40(uint256 argOffset) internal pure returns (uint40 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(216, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint32.
function _getArgUint32(uint256 argOffset) internal pure returns (uint32 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(224, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint24.
function _getArgUint24(uint256 argOffset) internal pure returns (uint24 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(232, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint16.
function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(240, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint8.
function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(248, calldataload(add(offset, argOffset)))
}
}
/// @return offset The offset of the packed immutable args in calldata.
function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
/// @solidity memory-safe-assembly
assembly {
offset := sub(calldatasize(), shr(240, calldataload(sub(calldatasize(), 2))))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title ISemver
/// @notice ISemver is a simple contract for ensuring that contracts are
/// versioned using semantic versioning.
interface ISemver {
/// @notice Getter for the semantic version of the contract. This is not
/// meant to be used onchain but instead meant to be used by offchain
/// tooling.
/// @return Semver contract version as a string.
function version() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IInitializable } from "interfaces/dispute/IInitializable.sol";
import { Timestamp, GameStatus, GameType, Claim, Hash } from "src/dispute/lib/Types.sol";
interface IDisputeGame is IInitializable {
event Resolved(GameStatus indexed status);
function createdAt() external view returns (Timestamp);
function resolvedAt() external view returns (Timestamp);
function status() external view returns (GameStatus);
function gameType() external view returns (GameType gameType_);
function gameCreator() external pure returns (address creator_);
function rootClaim() external pure returns (Claim rootClaim_);
function l1Head() external pure returns (Hash l1Head_);
function l2BlockNumber() external pure returns (uint256 l2BlockNumber_);
function extraData() external pure returns (bytes memory extraData_);
function resolve() external returns (GameStatus status_);
function gameData() external view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_);
function wasRespectedGameTypeWhenCreated() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Libraries
import {
Position,
Hash,
GameType,
VMStatus,
Timestamp,
Duration,
Clock,
GameId,
Claim,
LibGameId,
LibClock
} from "src/dispute/lib/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 output root and the L2 block number at which it was generated.
/// @custom:field root The output root.
/// @custom:field l2BlockNumber The L2 block number at which the output root was generated.
struct OutputRoot {
Hash root;
uint256 l2BlockNumber;
}
/// @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 OP Succinct
GameType internal constant OP_SUCCINCT = GameType.wrap(6);
/// @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;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Libraries
import { GameType, Hash, Claim } from "src/dispute/lib/LibUDT.sol";
////////////////////////////////////////////////////////////////
// `DisputeGameFactory` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when a dispute game is attempted to be created with an unsupported game type.
/// @param gameType The unsupported game type.
error NoImplementation(GameType gameType);
/// @notice Thrown when a dispute game that already exists is attempted to be created.
/// @param uuid The UUID of the dispute game that already exists.
error GameAlreadyExists(Hash uuid);
/// @notice Thrown when the root claim has an unexpected VM status.
/// Some games can only start with a root-claim with a specific status.
/// @param rootClaim is the claim that was unexpected.
error UnexpectedRootClaim(Claim rootClaim);
////////////////////////////////////////////////////////////////
// `FaultDisputeGame` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when a dispute game has already been initialized.
error AlreadyInitialized();
/// @notice Thrown when a supplied bond is not equal to the required bond amount to cover the cost of the interaction.
error IncorrectBondAmount();
/// @notice Thrown when a credit claim is attempted for a value of 0.
error NoCreditToClaim();
/// @notice Thrown when the transfer of credit to a recipient account reverts.
error BondTransferFailed();
/// @notice Thrown when the `extraData` passed to the CWIA proxy is of improper length, or contains invalid information.
error BadExtraData();
/// @notice Thrown when a defense against the root claim is attempted.
error CannotDefendRootClaim();
/// @notice Thrown when a claim is attempting to be made that already exists.
error ClaimAlreadyExists();
/// @notice Thrown when a disputed claim does not match its index in the game.
error InvalidDisputedClaimIndex();
/// @notice Thrown when an action that requires the game to be `IN_PROGRESS` is invoked when
/// the game is not in progress.
error GameNotInProgress();
/// @notice Thrown when a move is attempted to be made after the clock has timed out.
error ClockTimeExceeded();
/// @notice Thrown when the game is attempted to be resolved too early.
error ClockNotExpired();
/// @notice Thrown when a move is attempted to be made at or greater than the max depth of the game.
error GameDepthExceeded();
/// @notice Thrown when a step is attempted above the maximum game depth.
error InvalidParent();
/// @notice Thrown when an invalid prestate is supplied to `step`.
error InvalidPrestate();
/// @notice Thrown when a step is made that computes the expected post state correctly.
error ValidStep();
/// @notice Thrown when a game is attempted to be initialized with an L1 head that does
/// not contain the disputed output root.
error L1HeadTooOld();
/// @notice Thrown when an invalid local identifier is passed to the `addLocalData` function.
error InvalidLocalIdent();
/// @notice Thrown when resolving claims out of order.
error OutOfOrderResolution();
/// @notice Thrown when resolving a claim that has already been resolved.
error ClaimAlreadyResolved();
/// @notice Thrown when a parent output root is attempted to be found on a claim that is in
/// the output root portion of the tree.
error ClaimAboveSplit();
/// @notice Thrown on deployment if the split depth is greater than or equal to the max
/// depth of the game.
error InvalidSplitDepth();
/// @notice Thrown on deployment if the max clock duration is less than or equal to the clock extension.
error InvalidClockExtension();
/// @notice Thrown on deployment if the PreimageOracle challenge period is too high.
error InvalidChallengePeriod();
/// @notice Thrown on deployment if the max depth is greater than `LibPosition.`
error MaxDepthTooLarge();
/// @notice Thrown when trying to step against a claim for a second time, after it has already been countered with
/// an instruction step.
error DuplicateStep();
/// @notice Thrown when an anchor root is not found for a given game type.
error AnchorRootNotFound();
/// @notice Thrown when an output root proof is invalid.
error InvalidOutputRootProof();
/// @notice Thrown when header RLP is invalid with respect to the block hash in an output root proof.
error InvalidHeaderRLP();
/// @notice Thrown when there is a match between the block number in the output root proof and the block number
/// claimed in the dispute game.
error BlockNumberMatches();
/// @notice Thrown when the L2 block number claim has already been challenged.
error L2BlockNumberChallenged();
/// @notice Thrown when the game is not yet finalized.
error GameNotFinalized();
/// @notice Thrown when an invalid bond distribution mode is supplied.
error InvalidBondDistributionMode();
/// @notice Thrown when the game is not yet resolved.
error GameNotResolved();
/// @notice Thrown when a reserved game type is used.
error ReservedGameType();
////////////////////////////////////////////////////////////////
// `PermissionedDisputeGame` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when an unauthorized address attempts to interact with the game.
error BadAuth();// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/Address.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
* initialization step. This is essential to configure modules that are added through upgrades and that require
* initialization.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Types
/// @notice Contains various types used throughout the Optimism contract system.
library Types {
/// @notice OutputProposal represents a commitment to the L2 state. The timestamp is the L1
/// timestamp that the output root is posted. This timestamp is used to verify that the
/// finalization period has passed since the output root was submitted.
/// @custom:field outputRoot Hash of the L2 output.
/// @custom:field timestamp Timestamp of the L1 block that the output root was submitted in.
/// @custom:field l2BlockNumber L2 block number that the output corresponds to.
struct OutputProposal {
bytes32 outputRoot;
uint128 timestamp;
uint128 l2BlockNumber;
}
/// @notice Struct representing the elements that are hashed together to generate an output root
/// which itself represents a snapshot of the L2 state.
/// @custom:field version Version of the output root.
/// @custom:field stateRoot Root of the state trie at the block of this output.
/// @custom:field messagePasserStorageRoot Root of the message passer storage trie.
/// @custom:field latestBlockhash Hash of the block this output was generated from.
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 messagePasserStorageRoot;
bytes32 latestBlockhash;
}
/// @notice Struct representing a deposit transaction (L1 => L2 transaction) created by an end
/// user (as opposed to a system deposit transaction generated by the system).
/// @custom:field from Address of the sender of the transaction.
/// @custom:field to Address of the recipient of the transaction.
/// @custom:field isCreation True if the transaction is a contract creation.
/// @custom:field value Value to send to the recipient.
/// @custom:field mint Amount of ETH to mint.
/// @custom:field gasLimit Gas limit of the transaction.
/// @custom:field data Data of the transaction.
/// @custom:field l1BlockHash Hash of the block the transaction was submitted in.
/// @custom:field logIndex Index of the log in the block the transaction was submitted in.
struct UserDepositTransaction {
address from;
address to;
bool isCreation;
uint256 value;
uint256 mint;
uint64 gasLimit;
bytes data;
bytes32 l1BlockHash;
uint256 logIndex;
}
/// @notice Struct representing a withdrawal transaction.
/// @custom:field nonce Nonce of the withdrawal transaction
/// @custom:field sender Address of the sender of the transaction.
/// @custom:field target Address of the recipient of the transaction.
/// @custom:field value Value to send to the recipient.
/// @custom:field gasLimit Gas limit of the transaction.
/// @custom:field data Data of the transaction.
struct WithdrawalTransaction {
uint256 nonce;
address sender;
address target;
uint256 value;
uint256 gasLimit;
bytes data;
}
/// @notice Enum representing where the FeeVault withdraws funds to.
/// @custom:value L1 FeeVault withdraws funds to L1.
/// @custom:value L2 FeeVault withdraws funds to L2.
enum WithdrawalNetwork {
L1,
L2
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// The game type for the OP Succinct Fault Dispute Game.
// Eventually will be enshrined in the game type enum.
uint32 constant OP_SUCCINCT_FAULT_DISPUTE_GAME_TYPE = 42;
/// @notice The public values committed to for an OP Succinct aggregation program.
struct AggregationOutputs {
bytes32 l1Head;
bytes32 l2PreRoot;
bytes32 claimRoot;
uint256 claimBlockNum;
bytes32 rollupConfigHash;
bytes32 rangeVkeyCommitment;
address proverAddress;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Interfaces
import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol";
/// @title Constants
/// @notice Constants is a library for storing constants. Simple! Don't put everything in here, just
/// the stuff used in multiple contracts. Constants that only apply to a single contract
/// should be defined in that contract instead.
library Constants {
/// @notice Special address to be used as the tx origin for gas estimation calls in the
/// OptimismPortal and CrossDomainMessenger calls. You only need to use this address if
/// the minimum gas limit specified by the user is not actually enough to execute the
/// given message and you're attempting to estimate the actual necessary gas limit. We
/// use address(1) because it's the ecrecover precompile and therefore guaranteed to
/// never have any code on any EVM chain.
address internal constant ESTIMATION_ADDRESS = address(1);
/// @notice Value used for the L2 sender storage slot in both the OptimismPortal and the
/// CrossDomainMessenger contracts before an actual sender is set. This value is
/// non-zero to reduce the gas cost of message passing transactions.
address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD;
/// @notice The storage slot that holds the address of a proxy implementation.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
bytes32 internal constant PROXY_OWNER_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice The address that represents ether when dealing with ERC20 token addresses.
address internal constant ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @notice The address that represents the system caller responsible for L1 attributes
/// transactions.
address internal constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
/// @notice Returns the default values for the ResourceConfig. These are the recommended values
/// for a production network.
function DEFAULT_RESOURCE_CONFIG() internal pure returns (IResourceMetering.ResourceConfig memory) {
IResourceMetering.ResourceConfig memory config = IResourceMetering.ResourceConfig({
maxResourceLimit: 20_000_000,
elasticityMultiplier: 10,
baseFeeMaxChangeDenominator: 8,
minimumBaseFee: 1 gwei,
systemTxMaxGas: 1_000_000,
maximumBaseFee: type(uint128).max
});
return config;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// @title SP1 Verifier Interface
/// @author Succinct Labs
/// @notice This contract is the interface for the SP1 Verifier.
interface ISP1Verifier {
/// @notice Verifies a proof with given public values and vkey.
/// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of
/// target verifier's VERIFIER_HASH.
/// @param programVKey The verification key for the RISC-V program.
/// @param publicValues The public values encoded as bytes.
/// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
function verifyProof(
bytes32 programVKey,
bytes calldata publicValues,
bytes calldata proofBytes
) external view;
}
interface ISP1VerifierWithHash is ISP1Verifier {
/// @notice Returns the hash of the verifier.
function VERIFIER_HASH() external pure returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol";
import { GameId, Timestamp, Claim, Hash, GameType } from "src/dispute/lib/Types.sol";
interface IDisputeGameFactory {
struct GameSearchResult {
uint256 index;
GameId metadata;
Timestamp timestamp;
Claim rootClaim;
bytes extraData;
}
error GameAlreadyExists(Hash uuid);
error IncorrectBondAmount();
error NoImplementation(GameType gameType);
event DisputeGameCreated(address indexed disputeProxy, GameType indexed gameType, Claim indexed rootClaim);
event ImplementationSet(address indexed impl, GameType indexed gameType);
event InitBondUpdated(GameType indexed gameType, uint256 indexed newBond);
event Initialized(uint8 version);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function create(
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
external
payable
returns (IDisputeGame proxy_);
function findLatestGames(
GameType _gameType,
uint256 _start,
uint256 _n
)
external
view
returns (GameSearchResult[] memory games_);
function gameAtIndex(uint256 _index)
external
view
returns (GameType gameType_, Timestamp timestamp_, IDisputeGame proxy_);
function gameCount() external view returns (uint256 gameCount_);
function gameImpls(GameType) external view returns (IDisputeGame);
function games(
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
external
view
returns (IDisputeGame proxy_, Timestamp timestamp_);
function getGameUUID(
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
external
pure
returns (Hash uuid_);
function initBonds(GameType) external view returns (uint256);
function initialize(address _owner) external;
function owner() external view returns (address);
function renounceOwnership() external;
function setImplementation(GameType _gameType, IDisputeGame _impl) external;
function setInitBond(GameType _gameType, uint256 _initBond) external;
function transferOwnership(address newOwner) external; // nosemgrep
function version() external view returns (string memory);
function __constructor__() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IInitializable {
function initialize() external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Libraries
import { Position } from "src/dispute/lib/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
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IResourceMetering {
struct ResourceParams {
uint128 prevBaseFee;
uint64 prevBoughtGas;
uint64 prevBlockNum;
}
struct ResourceConfig {
uint32 maxResourceLimit;
uint8 elasticityMultiplier;
uint8 baseFeeMaxChangeDenominator;
uint32 minimumBaseFee;
uint32 systemTxMaxGas;
uint128 maximumBaseFee;
}
error OutOfGas();
event Initialized(uint8 version);
function params() external view returns (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum); // nosemgrep
function __constructor__() external;
}// 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
}
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@optimism/=lib/optimism/packages/contracts-bedrock/",
"@forge-std/=lib/forge-std/src/",
"@solady/=lib/solady/src/",
"@sp1-contracts/=lib/sp1-contracts/contracts/",
"@rari-capital/=lib/optimism/packages/contracts-bedrock/lib/",
"src/libraries/=lib/optimism/packages/contracts-bedrock/src/libraries/",
"src/L1/=lib/optimism/packages/contracts-bedrock/src/L1/",
"src/L2/=lib/optimism/packages/contracts-bedrock/src/L2/",
"src/dispute/=lib/optimism/packages/contracts-bedrock/src/dispute/",
"src/cannon/=lib/optimism/packages/contracts-bedrock/src/cannon/",
"interfaces/=lib/optimism/packages/contracts-bedrock/interfaces/",
"@lib-keccak/=lib/lib-keccak/contracts/lib/",
"@solady-test/=lib/lib-keccak/lib/solady/test/",
"ds-test/=lib/solady/lib/ds-test/src/",
"erc4626-tests/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-v5/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src/",
"lib-keccak/=lib/lib-keccak/contracts/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts-v5/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-v5/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"optimism/=lib/optimism/",
"safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
"solady-v0.0.245/=lib/optimism/packages/contracts-bedrock/lib/solady-v0.0.245/src/",
"solady/=lib/solady/",
"solmate/=lib/optimism/packages/contracts-bedrock/lib/solmate/src/",
"sp1-contracts/=lib/sp1-contracts/contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"_l2OutputOracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"GameNotInProgress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum GameStatus","name":"status","type":"uint8"}],"name":"Resolved","type":"event"},{"inputs":[],"name":"configName","outputs":[{"internalType":"bytes32","name":"configName_","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"createdAt","outputs":[{"internalType":"Timestamp","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"extraData","outputs":[{"internalType":"bytes","name":"extraData_","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"gameCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"gameData","outputs":[{"internalType":"GameType","name":"gameType_","type":"uint32"},{"internalType":"Claim","name":"rootClaim_","type":"bytes32"},{"internalType":"bytes","name":"extraData_","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"gameType","outputs":[{"internalType":"GameType","name":"","type":"uint32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"l1BlockNumber","outputs":[{"internalType":"uint256","name":"l1BlockNumber_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"l1Head","outputs":[{"internalType":"Hash","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"l2BlockNumber","outputs":[{"internalType":"uint256","name":"l2BlockNumber_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"l2OutputOracle","outputs":[{"internalType":"address","name":"l2OutputOracle_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proof","outputs":[{"internalType":"bytes","name":"proof_","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"proverAddress","outputs":[{"internalType":"address","name":"proverAddress_","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"resolve","outputs":[{"internalType":"enum GameStatus","name":"status_","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resolvedAt","outputs":[{"internalType":"Timestamp","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rootClaim","outputs":[{"internalType":"Claim","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"status","outputs":[{"internalType":"enum GameStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wasRespectedGameTypeWhenCreated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.