Source Code
Overview
ETH Balance
0.054 ETH
Eth Value
$173.08 (@ $3,205.25/ETH)Latest 7 from a total of 7 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Prove Withdrawal | 23191279 | 84 days ago | IN | 0 ETH | 0.00056427 | ||||
| Initiate Deposit | 23123347 | 94 days ago | IN | 0.05 ETH | 0.00003106 | ||||
| Initiate Deposit | 23119644 | 94 days ago | IN | 0.001 ETH | 0.00018226 | ||||
| Initiate Deposit | 23119570 | 94 days ago | IN | 0.002 ETH | 0.00019424 | ||||
| Transfer | 23084544 | 99 days ago | IN | 0.001 ETH | 0.00018313 | ||||
| Renounce Ownersh... | 23084464 | 99 days ago | IN | 0 ETH | 0.0000072 | ||||
| Set L2Bridge | 23084462 | 99 days ago | IN | 0 ETH | 0.00001588 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
L1Bridge
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {Types} from "src/libraries/Types.sol";
import {Hashing} from "src/libraries/Hashing.sol";
import {SecureMerkleTrie} from "src/libraries/trie/SecureMerkleTrie.sol";
import {LibFacet} from "facet-sol/src/utils/LibFacet.sol";
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {L2Bridge} from "src/L2Bridge.sol";
import {Rollup} from "src/Rollup.sol";
import {EOA} from "src/facet-libraries/EOA.sol";
/**
* @title L1Bridge
* @notice L1 ETH bridge that accepts deposits and uses ZK proofs to verify withdrawal claims.
*
* @dev CRITICAL ARCHITECTURE NOTICE FOR USERS:
*
* This bridge verifies withdrawals using ZK proofs from a Rollup contract. Unlike bridges that
* rely on upgradeable proof systems, each Rollup contract here is immutable - hardcoded to prove
* one specific state transition function forever.
*
* KEY IMPLICATIONS:
*
* 1. FORK HANDLING: When the L2 network upgrades, this bridge won't automatically recognize the
* new rules. The bridge owner must call setRollup() to point to a new Rollup contract that
* proves the updated state transition function.
*
* 2. OWNERSHIP TRADE-OFFS:
* - With active owner: Can adapt to forks but requires trusting the owner won't set a
* malicious rollup contract
* - With renounced ownership: Becomes trustless* with respect to human operators,
* but permanently locked to a single fork's rules (*Security depends solely on the ZK proof
* system and smart contract correctness)
*
* 3. TRUST MODELS: Users can choose between:
* - Active ownership: Trust a human operator to handle forks properly (more flexible)
* - Renounced ownership: Trust only the code and ZK proofs (more secure but less flexible)
*
* 4. FORK INCOMPATIBILITY: If the L2 network forks and modifies how bridged assets work, but
* this bridge isn't updated to point to a new Rollup contract, those modifications won't be
* reflected in withdrawal capabilities. Your assets remain subject to the original rules.
*
* RECOMMENDATION: Before depositing, verify:
* - Current owner status: check owner() - address(0) means renounced
* - If owned: research the owner's reputation and track record
* - Which Rollup contract is currently being used (check rollup() function)
* - Your preference: human-free operation vs flexibility for upgrades
*/
contract L1Bridge is Ownable, ReentrancyGuard, Pausable {
using SafeTransferLib for address;
/*//////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Represents a deposit transaction that can be replayed if FACET blocks are full
* @dev This struct enables deposit retry functionality when L2 blocks hit gas limits
* @param nonce Unique global nonce for the deposit, used for replay verification
* @param to Address that will receive the deposit on L2
* @param amount Amount of ETH being deposited (in wei)
*/
struct DepositTransaction {
uint256 nonce;
address to;
uint256 amount;
}
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/
error L2BridgeNotSet();
error WithdrawalAlreadyProven();
error WithdrawalAlreadyFinalized();
error ProposalNotCanonical();
error InvalidOutputRoot();
error InvalidWithdrawalProof();
error WithdrawalNotProven();
error InvalidDepositAmount();
error L2BridgeAlreadySet();
error RootBlacklisted();
error WithdrawalDelayNotMet();
error InvalidDepositParameters();
error InvalidNonce();
error OnlyCanDepositWithoutTo();
/*//////////////////////////////////////////////////////////////
CONFIG
//////////////////////////////////////////////////////////////*/
Rollup public rollup;
address public l2Bridge;
// Training wheels
mapping(bytes32 => bool) public rootBlacklisted;
uint256 public withdrawalDelay; // seconds
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
struct ProvenWithdrawal {
uint32 proposalId;
uint32 provenAt;
}
mapping(bytes32 => mapping(Rollup => ProvenWithdrawal)) public proven;
mapping(bytes32 => bool) public finalized;
/**
* @notice Stores hashes of deposit parameters for replay verification
* @dev Maps nonce => keccak256(nonce, to, amount) to ensure replays use identical parameters
*/
mapping(uint256 => bytes32) public depositHashes;
/**
* @notice Global nonce counter for deposits
* @dev Incremented for each new deposit to ensure uniqueness
*/
uint256 public depositNonce;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event DepositInitiated(uint256 indexed nonce, address indexed from, address indexed to, uint256 amount);
event DepositReplayed(uint256 indexed nonce, address indexed to, uint256 amount);
event WithdrawalProven(address indexed rollup, address indexed to, uint256 amount, uint256 nonce, uint256 proposalId);
event WithdrawalFinalized(address indexed to, uint256 amount, uint256 nonce);
event RollupUpdated(address indexed oldRollup, address indexed newRollup);
event RootBlacklistStatusChanged(bytes32 indexed root, bool blacklisted);
event WithdrawalDelayUpdated(uint256 oldDelay, uint256 newDelay);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
// storage key slot used by the L2ToL1MessagePasser contract
bytes32 internal constant MESSAGE_PASSER_SLOT = bytes32(uint256(0));
// Gas limit for FACET transactions
uint256 internal constant DEPOSIT_GAS_LIMIT = 500_000;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(Rollup _rollup) {
rollup = _rollup;
}
function setL2Bridge(address _l2Bridge) external onlyOwner {
if (l2Bridge != address(0)) revert L2BridgeAlreadySet();
l2Bridge = _l2Bridge;
}
/*//////////////////////////////////////////////////////////////
TRAINING WHEELS
//////////////////////////////////////////////////////////////*/
/**
* @notice Update the rollup contract reference to support new forks or state transition rules.
* @dev CRITICAL: This function enables fork flexibility but also represents the primary
* trust assumption. If ownership is renounced, this function becomes inaccessible,
* making the bridge trustless (no human control) but locked to the current state transition rules.
* @param _rollup New rollup contract address that proves the updated state transition rules
*/
function setRollup(address _rollup) external onlyOwner {
address oldRollup = address(rollup);
rollup = Rollup(_rollup);
emit RollupUpdated(oldRollup, _rollup);
}
/**
* @notice Pause the bridge
*/
function pause() external onlyOwner {
_pause();
}
/**
* @notice Unpause the bridge
*/
function unpause() external onlyOwner {
_unpause();
}
/**
* @notice Blacklist or unblacklist a root
* @param root The root to blacklist/unblacklist
* @param blacklisted True to blacklist, false to unblacklist
*/
function setRootBlacklisted(bytes32 root, bool blacklisted) external onlyOwner {
rootBlacklisted[root] = blacklisted;
emit RootBlacklistStatusChanged(root, blacklisted);
}
/**
* @notice Update withdrawal delay period
* @param _withdrawalDelay New delay in seconds
*/
function setWithdrawalDelay(uint256 _withdrawalDelay) external onlyOwner {
uint256 oldDelay = withdrawalDelay;
withdrawalDelay = _withdrawalDelay;
emit WithdrawalDelayUpdated(oldDelay, _withdrawalDelay);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT
//////////////////////////////////////////////////////////////*/
/**
* @notice Initiates a new deposit to L2 that can be replayed if FACET block is full
* @dev Stores a hash of deposit parameters to enable safe replay with identical parameters.
* This is crucial for FACET integration where blocks may be full due to gas limits.
* @param to Address that will receive the deposit on L2
* @return nonce Unique nonce for this deposit that must be used for any replay attempts
*/
function initiateDeposit(address to) public payable virtual whenNotPaused returns (uint256 nonce) {
if (l2Bridge == address(0)) revert L2BridgeNotSet();
if (msg.value == 0) revert InvalidDepositAmount();
// Increment nonce and store deposit hash
nonce = ++depositNonce;
// Create deposit transaction struct
DepositTransaction memory deposit = DepositTransaction({
nonce: nonce,
to: to,
amount: msg.value
});
// Store hash of deposit for replay verification
depositHashes[nonce] = _hashDeposit(deposit);
// Send the deposit message to L2
_sendDepositToL2(deposit);
emit DepositInitiated(nonce, msg.sender, to, msg.value);
}
/**
* @notice Replays a previously initiated deposit with identical parameters
* @dev Used when the initial deposit attempt failed due to FACET block being full.
* Verifies that the deposit parameters match exactly what was originally stored.
* This function does NOT require msg.value as it uses the originally deposited ETH.
* @param deposit The deposit transaction to replay (must match original parameters exactly)
*/
function replayDeposit(
DepositTransaction calldata deposit
) external virtual whenNotPaused {
// Verify nonce exists and parameters match
bytes32 storedHash = depositHashes[deposit.nonce];
bytes32 paramsHash = _hashDeposit(deposit);
if (storedHash != paramsHash) revert InvalidDepositParameters();
// Send the same deposit message to L2
_sendDepositToL2(deposit);
emit DepositReplayed(deposit.nonce, deposit.to, deposit.amount);
}
/**
* @notice Internal function to send deposit message to L2
* @dev Uses LibFacet to send cross-chain message to FACET L2
* @param deposit The deposit transaction to send to L2
*/
function _sendDepositToL2(
DepositTransaction memory deposit
) internal {
bytes memory data = abi.encodeWithSelector(
L2Bridge.finalizeDeposit.selector,
deposit
);
LibFacet.sendFacetTransaction({to: l2Bridge, gasLimit: DEPOSIT_GAS_LIMIT, data: data});
}
/**
* @notice Allows EOAs to deposit ETH by sending it directly to the bridge
* @dev Restricted to EOAs only to prevent aliasing issues with contract addresses.
* When an EOA sends ETH directly, it's deposited to their own address on L2.
* Includes EIP-7702 delegated EOA support.
*/
receive() external payable {
if (!EOA.isSenderEOA()) revert OnlyCanDepositWithoutTo();
// For EOAs depositing via receive, deposit to themselves
initiateDeposit(msg.sender);
}
/*//////////////////////////////////////////////////////////////
WITHDRAWAL – PROVE
//////////////////////////////////////////////////////////////*/
/**
* @notice Prove a withdrawal by verifying merkle proof against canonical L2 state
* @dev This verification is bound to the current rollup contract's state transition rules.
* If the L2 forks but this bridge's rollup reference isn't updated, withdrawals
* must still conform to the original state transition function's rules.
* @param amount Amount of tokens to withdraw
* @param to Recipient address on L1
* @param nonce Withdrawal nonce from L2
* @param proposalId The canonical proposal ID from current Rollup contract
* @param rootProof The merkle proof components from the L2 output root
* @param withdrawalProof Merkle proof path in the L2ToL1MessagePasser storage trie
*/
function proveWithdrawal(
address to,
uint256 amount,
uint256 nonce,
uint256 proposalId,
Types.OutputRootProof calldata rootProof,
bytes[] calldata withdrawalProof
) external virtual whenNotPaused {
bytes32 withdrawalHash = _hashWithdrawal(to, amount, nonce);
ProvenWithdrawal storage info = proven[withdrawalHash][rollup];
if (info.provenAt != 0) revert WithdrawalAlreadyProven();
if (finalized[withdrawalHash]) revert WithdrawalAlreadyFinalized();
if (!rollup.proposalIsCanonical(proposalId)) revert ProposalNotCanonical();
Rollup.Proposal memory prop = rollup.getProposal(proposalId);
// Check if root is blacklisted
if (rootBlacklisted[prop.rootClaim]) revert RootBlacklisted();
if (prop.rootClaim != Hashing.hashOutputRootProof(rootProof)) revert InvalidOutputRoot();
// verify inclusion of message in L2 storage
bytes32 storageKey = keccak256(abi.encode(withdrawalHash, uint256(0))); // slot 0
bool valid = SecureMerkleTrie.verifyInclusionProof({
_key: abi.encode(storageKey),
_value: hex"01", // value of 1 indicates withdrawal exists
_proof: withdrawalProof,
_root: rootProof.messagePasserStorageRoot
});
if (!valid) revert InvalidWithdrawalProof();
proven[withdrawalHash][rollup] = ProvenWithdrawal({
proposalId: uint32(proposalId),
provenAt: uint32(block.timestamp)
});
emit WithdrawalProven(address(rollup), to, amount, nonce, proposalId);
}
/*//////////////////////////////////////////////////////////////
WITHDRAWAL – FINALIZE
//////////////////////////////////////////////////////////////*/
function finalizeWithdrawal(address to, uint256 amount, uint256 nonce) external nonReentrant whenNotPaused {
bytes32 withdrawalHash = _hashWithdrawal(to, amount, nonce);
ProvenWithdrawal storage info = proven[withdrawalHash][rollup];
if (info.provenAt == 0) revert WithdrawalNotProven();
if (finalized[withdrawalHash]) revert WithdrawalAlreadyFinalized();
// Respect safety delay
if (block.timestamp <= info.provenAt + withdrawalDelay) revert WithdrawalDelayNotMet();
// Check if the root of the proposal used for proving is blacklisted
Rollup.Proposal memory prop = rollup.getProposal(info.proposalId);
if (rootBlacklisted[prop.rootClaim]) revert RootBlacklisted();
finalized[withdrawalHash] = true;
to.forceSafeTransferETH(amount, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES);
emit WithdrawalFinalized(to, amount, nonce);
}
/*//////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/
function _hashWithdrawal(address to, uint256 amount, uint256 nonce) internal view returns (bytes32) {
bytes memory data = abi.encode(to, amount);
Types.WithdrawalTransaction memory w = Types.WithdrawalTransaction({
nonce: nonce,
sender: l2Bridge,
target: address(this),
value: 0,
gasLimit: 0,
data: data
});
return Hashing.hashWithdrawal(w);
}
/**
* @notice Internal function to hash a deposit transaction for replay verification
* @dev Creates a unique hash from deposit parameters to ensure replay safety
* @param deposit The deposit transaction to hash
* @return Hash of the deposit transaction (keccak256 of encoded parameters)
*/
function _hashDeposit(DepositTransaction memory deposit) internal pure returns (bytes32) {
return keccak256(abi.encode(deposit.nonce, deposit.to, deposit.amount));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// 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.0;
// Libraries
import { Types } from "src/libraries/Types.sol";
import { Encoding } from "src/libraries/Encoding.sol";
/// @title Hashing
/// @notice Hashing handles Optimism's various different hashing schemes.
library Hashing {
/// @notice Computes the hash of the RLP encoded L2 transaction that would be generated when a
/// given deposit is sent to the L2 system. Useful for searching for a deposit in the L2
/// system.
/// @param _tx User deposit transaction to hash.
/// @return Hash of the RLP encoded L2 deposit transaction.
function hashDepositTransaction(Types.UserDepositTransaction memory _tx) internal pure returns (bytes32) {
return keccak256(Encoding.encodeDepositTransaction(_tx));
}
/// @notice Computes the deposit transaction's "source hash", a value that guarantees the hash
/// of the L2 transaction that corresponds to a deposit is unique and is
/// deterministically generated from L1 transaction data.
/// @param _l1BlockHash Hash of the L1 block where the deposit was included.
/// @param _logIndex The index of the log that created the deposit transaction.
/// @return Hash of the deposit transaction's "source hash".
function hashDepositSource(bytes32 _l1BlockHash, uint256 _logIndex) internal pure returns (bytes32) {
bytes32 depositId = keccak256(abi.encode(_l1BlockHash, _logIndex));
return keccak256(abi.encode(bytes32(0), depositId));
}
/// @notice Hashes the cross domain message based on the version that is encoded into the
/// message nonce.
/// @param _nonce Message nonce with version encoded into the first two bytes.
/// @param _sender Address of the sender of the message.
/// @param _target Address of the target of the message.
/// @param _value ETH value to send to the target.
/// @param _gasLimit Gas limit to use for the message.
/// @param _data Data to send with the message.
/// @return Hashed cross domain message.
function hashCrossDomainMessage(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
)
internal
pure
returns (bytes32)
{
(, uint16 version) = Encoding.decodeVersionedNonce(_nonce);
if (version == 0) {
return hashCrossDomainMessageV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return hashCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data);
} else {
revert("Hashing: unknown cross domain message version");
}
}
/// @notice Hashes a cross domain message based on the V0 (legacy) encoding.
/// @param _target Address of the target of the message.
/// @param _sender Address of the sender of the message.
/// @param _data Data to send with the message.
/// @param _nonce Message nonce.
/// @return Hashed cross domain message.
function hashCrossDomainMessageV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
)
internal
pure
returns (bytes32)
{
return keccak256(Encoding.encodeCrossDomainMessageV0(_target, _sender, _data, _nonce));
}
/// @notice Hashes a cross domain message based on the V1 (current) encoding.
/// @param _nonce Message nonce.
/// @param _sender Address of the sender of the message.
/// @param _target Address of the target of the message.
/// @param _value ETH value to send to the target.
/// @param _gasLimit Gas limit to use for the message.
/// @param _data Data to send with the message.
/// @return Hashed cross domain message.
function hashCrossDomainMessageV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
)
internal
pure
returns (bytes32)
{
return keccak256(Encoding.encodeCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data));
}
/// @notice Derives the withdrawal hash according to the encoding in the L2 Withdrawer contract
/// @param _tx Withdrawal transaction to hash.
/// @return Hashed withdrawal transaction.
function hashWithdrawal(Types.WithdrawalTransaction memory _tx) internal pure returns (bytes32) {
return keccak256(abi.encode(_tx.nonce, _tx.sender, _tx.target, _tx.value, _tx.gasLimit, _tx.data));
}
/// @notice Hashes the various elements of an output root proof into an output root hash which
/// can be used to check if the proof is valid.
/// @param _outputRootProof Output root proof which should hash to an output root.
/// @return Hashed output root proof.
function hashOutputRootProof(Types.OutputRootProof memory _outputRootProof) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_outputRootProof.version,
_outputRootProof.stateRoot,
_outputRootProof.messagePasserStorageRoot,
_outputRootProof.latestBlockhash
)
);
}
/// @notice Generates a unique hash for cross l2 messages. This hash is used to identify
/// the message and ensure it is not relayed more than once.
/// @param _destination Chain ID of the destination chain.
/// @param _source Chain ID of the source chain.
/// @param _nonce Unique nonce associated with the message to prevent replay attacks.
/// @param _sender Address of the user who originally sent the message.
/// @param _target Address of the contract or wallet that the message is targeting on the destination chain.
/// @param _message The message payload to be relayed to the target on the destination chain.
/// @return Hash of the encoded message parameters, used to uniquely identify the message.
function hashL2toL2CrossDomainMessage(
uint256 _destination,
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes memory _message
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(_destination, _source, _nonce, _sender, _target, _message));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries
import { MerkleTrie } from "src/libraries/trie/MerkleTrie.sol";
/// @title SecureMerkleTrie
/// @notice SecureMerkleTrie is a thin wrapper around the MerkleTrie library that hashes the input
/// keys. Ethereum's state trie hashes input keys before storing them.
library SecureMerkleTrie {
/// @notice Verifies a proof that a given key/value pair is present in the Merkle trie.
/// @param _key Key of the node to search for, as a hex string.
/// @param _value Value of the node to search for, as a hex string.
/// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle
/// trees, this proof is executed top-down and consists of a list of RLP-encoded
/// nodes that make a path down to the target node.
/// @param _root Known root of the Merkle trie. Used to verify that the included proof is
/// correctly constructed.
/// @return valid_ Whether or not the proof is valid.
function verifyInclusionProof(
bytes memory _key,
bytes memory _value,
bytes[] memory _proof,
bytes32 _root
)
internal
pure
returns (bool valid_)
{
bytes memory key = _getSecureKey(_key);
valid_ = MerkleTrie.verifyInclusionProof(key, _value, _proof, _root);
}
/// @notice Retrieves the value associated with a given key.
/// @param _key Key to search for, as hex bytes.
/// @param _proof Merkle trie inclusion proof for the key.
/// @param _root Known root of the Merkle trie.
/// @return value_ Value of the key if it exists.
function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) {
bytes memory key = _getSecureKey(_key);
value_ = MerkleTrie.get(key, _proof, _root);
}
/// @notice Computes the hashed version of the input key.
/// @param _key Key to hash.
/// @return hash_ Hashed version of the key.
function _getSecureKey(bytes memory _key) private pure returns (bytes memory hash_) {
hash_ = abi.encodePacked(keccak256(_key));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { LibRLP } from "../../lib/solady/src/utils/LibRLP.sol";
library LibFacet {
using LibRLP for LibRLP.List;
address constant facetInboxAddress = 0x00000000000000000000000000000000000FacE7;
bytes32 constant facetEventSignature = 0x00000000000000000000000000000000000000000000000000000000000face7;
uint8 constant facetTxType = 0x46;
function sendFacetTransaction(
uint256 gasLimit,
bytes memory data
) internal {
sendFacetTransaction({
to: bytes(''),
value: 0,
gasLimit: gasLimit,
data: data,
mineBoost: bytes('')
});
}
function sendFacetTransaction(
address to,
uint256 gasLimit,
bytes memory data
) internal {
sendFacetTransaction({
to: abi.encodePacked(to),
value: 0,
gasLimit: gasLimit,
data: data,
mineBoost: bytes('')
});
}
function prepareFacetTransaction(
uint256 chainId,
bytes memory to,
uint256 value,
uint256 gasLimit,
bytes memory data,
bytes memory mineBoost
) internal pure returns (bytes memory) {
LibRLP.List memory list;
list.p(chainId);
list.p(to);
list.p(value);
list.p(gasLimit);
list.p(data);
list.p(mineBoost);
return abi.encodePacked(facetTxType, list.encode());
}
function prepareFacetTransaction(
bytes memory to,
uint256 value,
uint256 gasLimit,
bytes memory data,
bytes memory mineBoost
) internal view returns (bytes memory) {
uint256 chainId;
if (block.chainid == 1) {
chainId = 0xface7;
} else if (block.chainid == 11155111) {
chainId = 0xface7a;
} else {
revert("Unsupported chainId");
}
return prepareFacetTransaction({
chainId: chainId,
to: to,
value: value,
gasLimit: gasLimit,
data: data,
mineBoost: mineBoost
});
}
function sendFacetTransaction(
bytes memory to,
uint256 value,
uint256 gasLimit,
bytes memory data,
bytes memory mineBoost
) internal {
bytes memory payload = prepareFacetTransaction({
to: to,
value: value,
gasLimit: gasLimit,
data: data,
mineBoost: mineBoost
});
assembly {
log1(add(payload, 32), mload(payload), facetEventSignature)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {AddressAliasHelper} from "optimism/packages/contracts-bedrock/src/vendor/AddressAliasHelper.sol";
import {L1Bridge} from "src/L1Bridge.sol";
interface IL2ToL1MessagePasser {
function initiateWithdrawal(address _target, uint256 _gasLimit, bytes calldata _data) external payable;
}
/**
* @title L2Bridge
* @notice L2 side of the ETH bridge that mints wrapped ETH tokens on FACET L2
* @dev This contract handles:
* - Finalizing deposits from L1 by minting wrapped ETH
* - Initiating withdrawals back to L1 by burning wrapped ETH
* - Replay protection to prevent double-spending of deposits
* Only the aliased L1 bridge address can finalize deposits.
*/
contract L2Bridge is ERC20 {
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/
error UnauthorizedBridge();
error InvalidWithdrawalAmount();
error InvalidL1Bridge();
error DepositAlreadyFinalized();
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
address public immutable l1Bridge;
IL2ToL1MessagePasser public constant MESSAGE_PASSER =
IL2ToL1MessagePasser(0x4200000000000000000000000000000000000016);
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @notice Tracks which deposit nonces have been finalized
* @dev Prevents replay attacks where the same deposit could be finalized multiple times.
* This is critical for FACET's retry mechanism - a deposit can be retried on L1
* but must only be finalized once on L2.
*/
mapping(uint256 => bool) public finalizedDeposits;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event DepositFinalized(uint256 indexed nonce, address indexed to, uint256 amount);
event WithdrawalInitiated(address indexed from, address indexed to, uint256 amount);
/**
* @notice Modifier to restrict functions to only the aliased L1 bridge
* @dev Ensures only legitimate cross-chain messages from L1 bridge can mint tokens
*/
modifier onlyL1Bridge() {
if (msg.sender != aliasedL1Bridge()) revert UnauthorizedBridge();
_;
}
constructor(string memory name_, string memory symbol_, address _l1Bridge) ERC20(name_, symbol_) {
if (_l1Bridge == address(0)) revert InvalidL1Bridge();
l1Bridge = _l1Bridge;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT
//////////////////////////////////////////////////////////////*/
/**
* @notice Finalizes a deposit from L1 by minting wrapped ETH to the recipient
* @dev Called by the L1 bridge via cross-chain message. Uses nonce-based replay protection
* to ensure each deposit is only finalized once, even if retried multiple times on L1
* due to FACET block gas limits.
* @param deposit The deposit transaction containing nonce, recipient, and amount
*/
function finalizeDeposit(
L1Bridge.DepositTransaction calldata deposit
) external onlyL1Bridge {
// Check if deposit has already been finalized
if (finalizedDeposits[deposit.nonce]) revert DepositAlreadyFinalized();
// Mark deposit as finalized
finalizedDeposits[deposit.nonce] = true;
// Mint tokens to recipient
_mint(deposit.to, deposit.amount);
emit DepositFinalized(deposit.nonce, deposit.to, deposit.amount);
}
/*//////////////////////////////////////////////////////////////
WITHDRAWAL
//////////////////////////////////////////////////////////////*/
/**
* @notice Initiates a withdrawal of wrapped ETH back to L1
* @dev Burns the wrapped ETH and sends a message to L1 via the L2ToL1MessagePasser.
* The withdrawal must be proven and finalized on L1 using merkle proofs.
* @param to The address that will receive the ETH on L1
* @param amount The amount of wrapped ETH to withdraw (burned on L2, received on L1)
*/
function initiateWithdrawal(address to, uint256 amount) external {
if (amount == 0) revert InvalidWithdrawalAmount();
_burn(msg.sender, amount);
bytes memory data = abi.encode(to, amount);
MESSAGE_PASSER.initiateWithdrawal(l1Bridge, 0, data);
emit WithdrawalInitiated(msg.sender, to, amount);
}
/**
* @notice Returns the aliased L1 bridge address
* @dev L1 to L2 messages have their sender address aliased for security.
* This function computes what the L1 bridge address becomes when aliased.
* @return The aliased address of the L1 bridge
*/
function aliasedL1Bridge() public view returns (address) {
return AddressAliasHelper.applyL1ToL2Alias(l1Bridge);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { ISP1Verifier } from "@sp1-contracts/src/ISP1Verifier.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { SafeCastLib } from "solady/src/utils/SafeCastLib.sol";
/// @title Rollup
/// @notice Dual-track ZK fault validity proof system
/// @dev Supports two tracks:
/// @dev 1. Fault proofs: Optimistic proposals that can be challenged (low cost)
/// @dev 2. Validity proofs: Direct ZK proofs that bypass challenges (instant finality)
/// @dev
/// @dev Key features:
/// @dev - Single-round dispute resolution.
/// @dev - Bulk invalidation: Validity proofs invalidate all conflicting fault proofs
/// @dev - Censorship resistance: Fallback window allows anyone to propose after timeout
contract Rollup is Ownable, ReentrancyGuard {
using SafeCastLib for uint256;
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
uint256 public immutable MAX_CHALLENGE_SECS;
uint256 public immutable MAX_PROVE_SECS;
uint256 public immutable CHALLENGER_BOND;
uint256 public immutable PROPOSER_BOND;
uint256 public immutable FALLBACK_TIMEOUT_SECS;
uint256 public immutable PROPOSAL_INTERVAL;
uint256 public immutable L2_START_TIMESTAMP;
uint256 public immutable L2_BLOCK_TIME;
ISP1Verifier public immutable VERIFIER;
bytes32 public immutable ROLLUP_CONFIG_HASH;
bytes32 public immutable AGG_VKEY;
bytes32 public immutable RANGE_VKEY_COMMITMENT;
string public constant version = "1.0.0";
/*//////////////////////////////////////////////////////////////
ENUMS
//////////////////////////////////////////////////////////////*/
enum ResolutionStatus { IN_PROGRESS, DEFENDER_WINS, CHALLENGER_WINS }
enum ProposalStatus {
Unchallenged,
Challenged,
UnchallengedAndProven,
ChallengedAndProven,
Resolved
}
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event ProposalSubmitted(
uint256 indexed proposalId,
uint256 indexed parentId,
address indexed proposer,
bytes32 root,
uint256 l2BlockNumber);
event ProposalChallenged(uint256 indexed proposalId, address indexed challenger);
event ProposalProven(uint256 indexed proposalId, address indexed prover);
event ProposalResolved(uint256 indexed proposalId, ResolutionStatus status);
event AnchorUpdated(uint256 indexed proposalId, bytes32 root, uint256 l2BlockNumber);
event ProposalClosed(uint256 indexed proposalId);
event ProposerPermissionUpdated(address indexed proposer, bool allowed);
event BlockProven(uint256 indexed l2BlockNumber, bytes32 root, address indexed prover);
event L1BlockHashCheckpointed(uint256 indexed l1BlockNumber, bytes32 blockHash);
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error BadAuth();
error IncorrectBondAmount();
error AlreadyChallenged();
error GameNotOver();
error GameOver();
error AlreadyResolved();
error NoCredit();
error TransferFailed();
error InvalidParentGame();
error BadCadence();
error ParentGameNotResolved();
error ProposingBackwards();
error ProposingFutureBlock();
error BlockAlreadyProven();
error L1BlockHashNotAvailable();
error L1BlockHashNotCheckpointed();
error NoCanonicalProposal();
error InvalidL2BlockNumber();
/*//////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
struct Proposal {
bytes32 rootClaim;
// packed slot
address proposer;
uint32 l2BlockNumber;
uint32 parentIndex;
uint32 deadline;
uint32 resolvedAt;
ProposalStatus proposalStatus;
ResolutionStatus resolutionStatus;
address challenger;
address prover; // Who proved this specific proposal
}
// No struct needed - just store the prover address directly
struct AggregationOutputs {
bytes32 l1Head;
bytes32 l2PreRoot;
bytes32 claimRoot;
uint256 claimBlockNum;
bytes32 rollupConfigHash;
bytes32 rangeVkeyCommitment;
address proverAddress;
}
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
Proposal[] proposals; // index == proposalId
mapping(address => uint256) public credit;
mapping(address => bool) public whitelistedProposer;
// The anchor tracks the latest accepted block
uint256 public anchorL2BlockNumber;
// Checkpointed L1 block hashes for proof verification
mapping(uint256 => bytes32) public l1BlockHashes;
// Maps L2 block number to the canonical proposal ID
// 0 means no canonical proposal exists (uninitialized storage)
// type(uint256).max means proposal 0 (genesis) is canonical
uint256 private constant GENESIS_SENTINEL = type(uint256).max;
mapping(uint256 => uint256) private _canonical;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
uint256 _challengeSecs,
uint256 _proveSecs,
uint256 _challengerBond,
uint256 _proposerBond,
uint256 _fallbackTimeout,
uint256 _proposalInterval,
bytes32 _startRoot,
uint256 _startBlock,
uint256 _l2StartTimestamp,
uint256 _l2BlockTime,
ISP1Verifier _verifier,
bytes32 _rollupHash,
bytes32 _aggVkey,
bytes32 _rangeCommit
) {
MAX_CHALLENGE_SECS = _challengeSecs;
MAX_PROVE_SECS = _proveSecs;
CHALLENGER_BOND = _challengerBond;
PROPOSER_BOND = _proposerBond;
FALLBACK_TIMEOUT_SECS = _fallbackTimeout;
PROPOSAL_INTERVAL = _proposalInterval;
L2_START_TIMESTAMP = _l2StartTimestamp;
L2_BLOCK_TIME = _l2BlockTime;
VERIFIER = _verifier;
ROLLUP_CONFIG_HASH = _rollupHash;
AGG_VKEY = _aggVkey;
RANGE_VKEY_COMMITMENT = _rangeCommit;
anchorL2BlockNumber = _startBlock;
// Create genesis proposal representing the starting anchor
Proposal memory genesis = Proposal({
rootClaim: _startRoot,
l2BlockNumber: _startBlock.toUint32(),
parentIndex: 0,
deadline: 0,
proposer: address(0),
challenger: address(0),
resolvedAt: 0,
proposalStatus: ProposalStatus.Resolved,
resolutionStatus: ResolutionStatus.DEFENDER_WINS,
prover: address(0)
});
proposals.push(genesis);
_trySetCanonical(_startBlock, 0);
}
/*//////////////////////////////////////////////////////////////
ACTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Creates a proposal with validation common to both fault proofs and validity proofs
/// @param root L2 output root being proposed
/// @param l2BlockNumber L2 block number (must be parentBlock + PROPOSAL_INTERVAL)
/// @param parentId Parent proposal ID (must be valid and not invalidated)
/// @param proposer Proposer address (address(0) for validity proofs, actual address for fault proofs)
/// @return proposalId ID of the created proposal
function _createProposal(
bytes32 root,
uint256 l2BlockNumber,
uint256 parentId,
address proposer
) internal returns (uint256 proposalId) {
if (parentId >= proposals.length) revert InvalidParentGame();
Proposal storage parent = proposals[parentId];
if (l2BlockNumber <= anchorL2BlockNumber) revert ProposingBackwards();
if (computeL2Timestamp(l2BlockNumber) >= block.timestamp) revert ProposingFutureBlock();
if (_canonicalExistsFor(l2BlockNumber)) revert BlockAlreadyProven();
// Proposals must follow the exact interval cadence from their parent
if (l2BlockNumber != parent.l2BlockNumber + PROPOSAL_INTERVAL) {
revert BadCadence();
}
// Cannot build on top of invalidated proposals
if (parent.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
revert InvalidParentGame();
}
proposals.push();
proposalId = proposals.length - 1;
Proposal storage p = proposals[proposalId];
p.rootClaim = root;
p.l2BlockNumber = l2BlockNumber.toUint32();
p.parentIndex = parentId.toUint32();
p.deadline = (block.timestamp + MAX_CHALLENGE_SECS).toUint32();
p.proposer = proposer;
emit ProposalSubmitted(proposalId, parentId, proposer, root, l2BlockNumber);
}
/// @notice Submit a fault proof proposal that can be challenged
/// @param root Output root claim for the L2 block
/// @param l2BlockNumber L2 block number being proposed
/// @param parentId Parent proposal to build upon
/// @return proposalId ID of created proposal
/// @dev Requires PROPOSER_BOND and proposer must be whitelisted or in fallback window
function submitProposal(
bytes32 root,
uint256 l2BlockNumber,
uint256 parentId
) external payable returns (uint256 proposalId) {
if (msg.value != PROPOSER_BOND) revert IncorrectBondAmount();
// Authorization: whitelist OR 2-week fallback (censorship resistance)
if (!isWhitelistedProposer(msg.sender) && !isInFallbackWindow(l2BlockNumber)) {
revert BadAuth();
}
proposalId = _createProposal({
root: root,
l2BlockNumber: l2BlockNumber,
parentId: parentId,
proposer: msg.sender
});
}
/// @notice Challenge a proposal claiming it has the wrong root
/// @param id Proposal ID to challenge
/// @dev Requires CHALLENGER_BOND, starts proof countdown
function challengeProposal(uint256 id) external payable onlyIfGameNotOver(id) {
if (msg.value != CHALLENGER_BOND) revert IncorrectBondAmount();
Proposal storage p = proposals[id];
if (p.proposalStatus != ProposalStatus.Unchallenged) revert AlreadyChallenged();
p.challenger = msg.sender;
p.proposalStatus = ProposalStatus.Challenged;
p.deadline = (block.timestamp + MAX_PROVE_SECS).toUint32();
emit ProposalChallenged(id, msg.sender);
}
/// @notice Submit a validity proof that bypasses the optimistic flow
/// @param l2BlockNumber L2 block to prove (must be anchorBlock + PROPOSAL_INTERVAL)
/// @param root Correct output root for this block
/// @param l1BlockNumber L1 block used in proof (for L1 data availability)
/// @param proof ZK proof of state transition from anchor to this block
/// @dev Creates, proves, and resolves a proposal atomically. No bond required.
function proveBlock(
uint256 l2BlockNumber,
bytes32 root,
uint256 l1BlockNumber,
bytes calldata proof
) external {
// Validity proofs must build directly on the anchor to ensure linear progression
// Anchor always has a canonical (genesis at minimum)
uint256 parentProposalId = anchorProposalId();
// address(0) proposer indicates no bond was collected
uint256 proposalId = _createProposal({
root: root,
l2BlockNumber: l2BlockNumber,
parentId: parentProposalId,
proposer: address(0)
});
proveProposal(proposalId, l1BlockNumber, proof);
resolveProposal(proposalId);
emit BlockProven(l2BlockNumber, root, msg.sender);
}
/// @notice Submit ZK proof defending a proposal
/// @param id Proposal to prove
/// @param l1BlockNumber L1 block for data availability context
/// @param proof ZK proof of state transition from parent to this proposal
/// @dev Parent must be valid for proof to be useful, but that's checked in resolution
function proveProposal(
uint256 id,
uint256 l1BlockNumber,
bytes calldata proof
) public onlyIfGameNotOver(id) {
Proposal storage p = proposals[id];
bytes32 parentRoot = proposals[p.parentIndex].rootClaim;
bytes32 l1BlockHash = getCheckpointedL1BlockHash(l1BlockNumber);
AggregationOutputs memory pub = AggregationOutputs({
l1Head: l1BlockHash,
l2PreRoot: parentRoot,
claimRoot: p.rootClaim,
claimBlockNum: p.l2BlockNumber,
rollupConfigHash: ROLLUP_CONFIG_HASH,
rangeVkeyCommitment: RANGE_VKEY_COMMITMENT,
proverAddress: msg.sender
});
VERIFIER.verifyProof(AGG_VKEY, abi.encode(pub), proof);
p.prover = msg.sender;
p.proposalStatus = (p.proposalStatus == ProposalStatus.Challenged)
? ProposalStatus.ChallengedAndProven
: ProposalStatus.UnchallengedAndProven;
emit ProposalProven(id, msg.sender);
}
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
modifier onlyIfGameOver(uint256 proposalId) {
if (!gameOver(proposalId)) revert GameNotOver();
_;
}
modifier onlyIfGameNotOver(uint256 proposalId) {
if (gameOver(proposalId)) revert GameOver();
_;
}
/*//////////////////////////////////////////////////////////////
GAME STATE LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Check if a proposal's game has ended (ready for resolution)
/// @param proposalId Proposal to check
/// @return True if deadline passed, proven locally, or canonical exists for block
function gameOver(uint256 proposalId) public view returns (bool) {
Proposal storage p = proposals[proposalId];
return p.deadline < block.timestamp ||
p.prover != address(0) ||
_canonicalExistsFor(p.l2BlockNumber);
}
/// @notice Calculate how long ago an L2 block should have been created
/// @param l2BlockNumber L2 block number
/// @return Age in seconds since the block's expected timestamp
function l2BlockAge(uint256 l2BlockNumber) public view returns (uint256) {
return block.timestamp - computeL2Timestamp(l2BlockNumber);
}
/// @notice Returns the L2 timestamp corresponding to a given L2 block number.
/// @notice Compute the expected timestamp for an L2 block
/// @param _l2BlockNumber L2 block number
/// @return Expected timestamp based on L2_BLOCK_TIME and genesis
function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
if (_l2BlockNumber < proposals[0].l2BlockNumber) {
revert InvalidL2BlockNumber();
}
return L2_START_TIMESTAMP + ((_l2BlockNumber - proposals[0].l2BlockNumber) * L2_BLOCK_TIME);
}
/// @notice Get L1 block hash from checkpoint or EVM history
/// @param l1BlockNumber L1 block number
/// @return l1BlockHash Block hash (reverts if too old and not checkpointed)
function getCheckpointedL1BlockHash(uint256 l1BlockNumber) internal view returns (bytes32 l1BlockHash) {
l1BlockHash = l1BlockHashes[l1BlockNumber];
if (l1BlockHash == bytes32(0)) {
revert L1BlockHashNotCheckpointed();
}
}
/// @notice Resolve a proposal determining if defender or challenger wins
/// @param id Proposal to resolve
/// @dev Resolution hierarchy:
/// @dev 1. Parent invalid → challenger wins (cascading invalidation)
/// @dev 2. Conflicts with canonical → challenger wins (bulk invalidation)
/// @dev 3. Has valid proof → defender wins
/// @dev 4. Timeout: challenged → challenger wins, unchallenged → defender wins
function resolveProposal(uint256 id) public onlyIfGameOver(id) {
Proposal storage p = proposals[id];
Proposal storage parent = proposals[p.parentIndex];
if (parent.resolutionStatus == ResolutionStatus.IN_PROGRESS) {
revert ParentGameNotResolved();
}
if (p.resolutionStatus != ResolutionStatus.IN_PROGRESS) {
revert AlreadyResolved();
}
// Resolution hierarchy (order matters!)
if (parent.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
}
else if (_proposalConflictsWithCanonical(p)) {
p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
}
else if (_getProposalProver(p) != address(0)) {
p.resolutionStatus = ResolutionStatus.DEFENDER_WINS;
}
else if (p.proposalStatus == ProposalStatus.Challenged) {
p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
} else {
p.resolutionStatus = ResolutionStatus.DEFENDER_WINS;
}
if (p.resolutionStatus == ResolutionStatus.DEFENDER_WINS) {
_trySetCanonical(p.l2BlockNumber, id);
// Advance anchor only if this directly extends it
if (p.l2BlockNumber == anchorL2BlockNumber + PROPOSAL_INTERVAL) {
anchorL2BlockNumber = p.l2BlockNumber;
emit AnchorUpdated(id, p.rootClaim, p.l2BlockNumber);
}
_pay(p.proposer, _proposerBond(p));
_pay(_getProposalProver(p), _challengerBond(p));
} else if (p.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
address recipient = p.challenger;
if (recipient == address(0)) {
// Bulk invalidation case: pay canonical prover who proved correct root
// If no canonical exists yet, bond is burned (recipient remains address(0))
if (_canonicalExistsFor(p.l2BlockNumber)) {
recipient = _getProposalProver(_canonicalProposalFor(p.l2BlockNumber));
}
}
_pay(recipient, _totalBond(p));
}
p.proposalStatus = ProposalStatus.Resolved;
p.resolvedAt = (block.timestamp).toUint32();
emit ProposalResolved(id, p.resolutionStatus);
emit ProposalClosed(id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL PAY-OUT HELPERS
//////////////////////////////////////////////////////////////*/
/// @dev Credits account
function _pay(address to, uint256 amount) internal {
if (to != address(0) && amount != 0) credit[to] += amount;
}
/// @dev Returns proposer bond amount (0 for validity proofs)
function _proposerBond(Proposal storage p) internal view returns (uint256) {
return p.proposer != address(0) ? PROPOSER_BOND : 0;
}
/// @dev Returns challenger bond amount (0 if unchallenged)
function _challengerBond(Proposal storage p) internal view returns (uint256) {
return p.challenger != address(0) ? CHALLENGER_BOND : 0;
}
/// @dev Total bonds at stake for this proposal
function _totalBond(Proposal storage p) internal view returns (uint256) {
return _proposerBond(p) + _challengerBond(p);
}
/// @dev Find who proved this proposal (considering canonical fallback)
/// @return Prover address or address(0) if conflicts with canonical
function _getProposalProver(Proposal storage p) internal view returns (address) {
if (_proposalConflictsWithCanonical(p)) {
return address(0);
}
// Prefer local prover, fall back to canonical proposal's prover
if (p.prover != address(0)) {
return p.prover;
}
if (_canonicalExistsFor(p.l2BlockNumber)) {
Proposal storage canonical = _canonicalProposalFor(p.l2BlockNumber);
return canonical.prover != address(0) ? canonical.prover : canonical.proposer;
}
return address(0);
}
/// @dev Check if proposal has wrong root compared to canonical
function _proposalConflictsWithCanonical(Proposal storage p) internal view returns (bool) {
return _canonicalExistsFor(p.l2BlockNumber) &&
_canonicalProposalFor(p.l2BlockNumber).rootClaim != p.rootClaim;
}
/*//////////////////////////////////////////////////////////////
CREDIT WITHDRAWAL
//////////////////////////////////////////////////////////////*/
/// @notice Withdraw accumulated credit from bonds
/// @param recipient Address to withdraw credit for
/// @dev Uses pull pattern to prevent reentrancy
function claimCredit(address recipient) public nonReentrant {
uint256 amount = credit[recipient];
if (amount == 0) revert NoCredit();
credit[recipient] = 0;
(bool ok,) = recipient.call{ value: amount }("");
if (!ok) revert TransferFailed();
}
/*//////////////////////////////////////////////////////////////
PERMISSIONS & AUTHORIZATION
//////////////////////////////////////////////////////////////*/
/// @notice Update proposer whitelist
/// @param proposer Address to update
/// @param allowed Whether address can propose
function setProposer(address proposer, bool allowed) external onlyOwner {
whitelistedProposer[proposer] = allowed;
emit ProposerPermissionUpdated(proposer, allowed);
}
/// @notice Check if address can submit proposals
/// @param proposer Address to check
/// @return True if whitelisted or wildcard enabled
function isWhitelistedProposer(address proposer) public view returns (bool) {
return whitelistedProposer[proposer] || whitelistedProposer[address(0)];
}
/// @notice Check if fallback window is active (anyone can propose)
/// @param l2BlockNumber Block to check
/// @return True if block is old enough for fallback
function isInFallbackWindow(uint256 l2BlockNumber) public view returns (bool) {
return l2BlockAge(l2BlockNumber) > FALLBACK_TIMEOUT_SECS;
}
/// @notice Store L1 block hash for proofs requiring old blocks
/// @param l1BlockNumber L1 block to checkpoint (must be recent)
/// @dev Only needed for blocks older than 256 blocks
function checkpointL1BlockHash(uint256 l1BlockNumber) external {
bytes32 blockHash = blockhash(l1BlockNumber);
if (blockHash == bytes32(0)) {
revert L1BlockHashNotAvailable();
}
l1BlockHashes[l1BlockNumber] = blockHash;
emit L1BlockHashCheckpointed(l1BlockNumber, blockHash);
}
/*//////////////////////////////////////////////////////////////
GETTERS
//////////////////////////////////////////////////////////////*/
/// @notice Get the current anchor's output root
/// @return Output root of the anchor block
function anchorRoot() public view returns (bytes32) {
// Anchor always has a canonical proposal (genesis at minimum)
return _canonicalProposalFor(anchorL2BlockNumber).rootClaim;
}
/// @notice Get a single proposal
/// @param id Proposal ID
/// @return The proposal struct
function getProposal(uint256 id) external view returns (Proposal memory) {
return proposals[id];
}
/// @notice Get current anchor root and block number
/// @return root Output root
/// @return blockNumber L2 block number
function getAnchorRoot() external view returns (bytes32, uint256) {
return (anchorRoot(), anchorL2BlockNumber);
}
/// @notice Batch get proposals by IDs
/// @param ids Array of proposal IDs
/// @return out Array of proposals
function getProposals(uint256[] calldata ids) external view returns (Proposal[] memory out) {
out = new Proposal[](ids.length);
for (uint256 i; i < ids.length; ++i) out[i] = proposals[ids[i]];
}
/// @notice Get most recent proposal IDs
/// @param count Number of proposals to return
/// @return ids Array of proposal IDs (newest first)
function latestProposals(uint256 count) external view returns (uint256[] memory ids) {
uint256 total = proposals.length;
if (count > total) count = total;
ids = new uint256[](count);
for (uint256 i; i < count; ++i) ids[i] = total - 1 - i;
}
/// @notice Get total number of proposals
/// @return Number of proposals created
function getProposalsLength() external view returns (uint256) {
return proposals.length;
}
/// @notice Check if proposal can be resolved
/// @param proposalId Proposal to check
/// @return True if game over and not yet resolved
function isResolvable(uint256 proposalId) external view returns (bool) {
if (proposalId >= proposals.length) return false;
Proposal storage p = proposals[proposalId];
return gameOver(proposalId) && p.resolutionStatus == ResolutionStatus.IN_PROGRESS;
}
/// @notice Check if proposal needs a proof
/// @param proposalId Proposal to check
/// @return True if challenged and deadline not passed
function needsDefense(uint256 proposalId) external view returns (bool) {
if (proposalId >= proposals.length) return false;
return !gameOver(proposalId) && proposals[proposalId].proposalStatus == ProposalStatus.Challenged;
}
/// @notice Get the canonical proposal ID for the anchor block
/// @return Proposal ID of current anchor
function anchorProposalId() public view returns (uint256) {
return canonicalProposalIdFor(anchorL2BlockNumber);
}
/*//////////////////////////////////////////////////////////////
CANONICAL PROPOSAL HELPERS
//////////////////////////////////////////////////////////////*/
/// @notice Get the canonical proposal for an L2 block
/// @param l2BlockNumber The L2 block number
/// @return The canonical proposal, or empty proposal if none exists
function canonicalProposalFor(uint256 l2BlockNumber) public view returns (Proposal memory) {
if (!_canonicalExistsFor(l2BlockNumber)) revert NoCanonicalProposal();
uint256 canonicalId = canonicalProposalIdFor(l2BlockNumber);
return proposals[canonicalId];
}
/// @notice Get the canonical proposal ID for an L2 block
/// @param l2BlockNumber The L2 block number
/// @return The canonical proposal ID, or 0 if none exists
function canonicalProposalIdFor(uint256 l2BlockNumber) public view returns (uint256) {
if (!_canonicalExistsFor(l2BlockNumber)) revert NoCanonicalProposal();
return _canonical[l2BlockNumber] == GENESIS_SENTINEL ? 0 : _canonical[l2BlockNumber];
}
function proposalIsCanonical(uint256 proposalId) public view returns (bool) {
if (proposalId >= proposals.length) return false;
Proposal storage p = proposals[proposalId];
return _canonicalExistsFor(p.l2BlockNumber) && proposalId == canonicalProposalIdFor(p.l2BlockNumber);
}
/// @dev Get the canonical proposal storage reference (reverts if doesn't exist)
function _canonicalProposalFor(uint256 l2BlockNumber) internal view returns (Proposal storage) {
uint256 canonicalId = canonicalProposalIdFor(l2BlockNumber);
return proposals[canonicalId];
}
/// @dev Check if a canonical proposal exists for an L2 block
function _canonicalExistsFor(uint256 l2BlockNumber) internal view returns (bool) {
return _canonical[l2BlockNumber] != 0;
}
/// @dev Try to set the canonical proposal for an L2 block (only sets if not already set)
function _trySetCanonical(uint256 l2BlockNumber, uint256 proposalId) internal {
if (!_canonicalExistsFor(l2BlockNumber)) {
_canonical[l2BlockNumber] = proposalId == 0 ? GENESIS_SENTINEL : proposalId;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**
* @title EOA
* @notice A library for detecting if an address is an EOA.
* @dev Includes support for EIP-7702 delegated EOAs
*/
library EOA {
/**
* @notice Returns true if sender address is an EOA.
* @dev Checks for regular EOAs and EIP-7702 delegated EOAs
* @return isEOA_ True if the sender address is an EOA.
*/
function isSenderEOA() internal view returns (bool isEOA_) {
if (msg.sender == tx.origin) {
// Regular EOA: sender is the transaction origin
isEOA_ = true;
} else if (address(msg.sender).code.length == 23) {
// Check for EIP-7702 delegated EOAs (23 bytes of code)
assembly {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0x20))
extcodecopy(caller(), ptr, 0, 0x20)
isEOA_ := eq(shr(232, mload(ptr)), 0xEF0100)
}
} else {
// If more or less than 23 bytes of code, not a 7702 delegated EOA
isEOA_ = false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries
import { Types } from "src/libraries/Types.sol";
import { Hashing } from "src/libraries/Hashing.sol";
import { RLPWriter } from "src/libraries/rlp/RLPWriter.sol";
/// @title Encoding
/// @notice Encoding handles Optimism's various different encoding schemes.
library Encoding {
/// @notice RLP encodes the L2 transaction that would be generated when a given deposit is sent
/// to the L2 system. Useful for searching for a deposit in the L2 system. The
/// transaction is prefixed with 0x7e to identify its EIP-2718 type.
/// @param _tx User deposit transaction to encode.
/// @return RLP encoded L2 deposit transaction.
function encodeDepositTransaction(Types.UserDepositTransaction memory _tx) internal pure returns (bytes memory) {
bytes32 source = Hashing.hashDepositSource(_tx.l1BlockHash, _tx.logIndex);
bytes[] memory raw = new bytes[](8);
raw[0] = RLPWriter.writeBytes(abi.encodePacked(source));
raw[1] = RLPWriter.writeAddress(_tx.from);
raw[2] = _tx.isCreation ? RLPWriter.writeBytes("") : RLPWriter.writeAddress(_tx.to);
raw[3] = RLPWriter.writeUint(_tx.mint);
raw[4] = RLPWriter.writeUint(_tx.value);
raw[5] = RLPWriter.writeUint(uint256(_tx.gasLimit));
raw[6] = RLPWriter.writeBool(false);
raw[7] = RLPWriter.writeBytes(_tx.data);
return abi.encodePacked(uint8(0x7e), RLPWriter.writeList(raw));
}
/// @notice Encodes the cross domain message based on the version that is encoded into the
/// message nonce.
/// @param _nonce Message nonce with version encoded into the first two bytes.
/// @param _sender Address of the sender of the message.
/// @param _target Address of the target of the message.
/// @param _value ETH value to send to the target.
/// @param _gasLimit Gas limit to use for the message.
/// @param _data Data to send with the message.
/// @return Encoded cross domain message.
function encodeCrossDomainMessage(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
)
internal
pure
returns (bytes memory)
{
(, uint16 version) = decodeVersionedNonce(_nonce);
if (version == 0) {
return encodeCrossDomainMessageV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return encodeCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data);
} else {
revert("Encoding: unknown cross domain message version");
}
}
/// @notice Encodes a cross domain message based on the V0 (legacy) encoding.
/// @param _target Address of the target of the message.
/// @param _sender Address of the sender of the message.
/// @param _data Data to send with the message.
/// @param _nonce Message nonce.
/// @return Encoded cross domain message.
function encodeCrossDomainMessageV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
)
internal
pure
returns (bytes memory)
{
// nosemgrep: sol-style-use-abi-encodecall
return abi.encodeWithSignature("relayMessage(address,address,bytes,uint256)", _target, _sender, _data, _nonce);
}
/// @notice Encodes a cross domain message based on the V1 (current) encoding.
/// @param _nonce Message nonce.
/// @param _sender Address of the sender of the message.
/// @param _target Address of the target of the message.
/// @param _value ETH value to send to the target.
/// @param _gasLimit Gas limit to use for the message.
/// @param _data Data to send with the message.
/// @return Encoded cross domain message.
function encodeCrossDomainMessageV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
)
internal
pure
returns (bytes memory)
{
// nosemgrep: sol-style-use-abi-encodecall
return abi.encodeWithSignature(
"relayMessage(uint256,address,address,uint256,uint256,bytes)",
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
}
/// @notice Adds a version number into the first two bytes of a message nonce.
/// @param _nonce Message nonce to encode into.
/// @param _version Version number to encode into the message nonce.
/// @return Message nonce with version encoded into the first two bytes.
function encodeVersionedNonce(uint240 _nonce, uint16 _version) internal pure returns (uint256) {
uint256 nonce;
assembly {
nonce := or(shl(240, _version), _nonce)
}
return nonce;
}
/// @notice Pulls the version out of a version-encoded nonce.
/// @param _nonce Message nonce with version encoded into the first two bytes.
/// @return Nonce without encoded version.
/// @return Version of the message.
function decodeVersionedNonce(uint256 _nonce) internal pure returns (uint240, uint16) {
uint240 nonce;
uint16 version;
assembly {
nonce := and(_nonce, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
version := shr(240, _nonce)
}
return (nonce, version);
}
/// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesEcotone
/// @param _baseFeeScalar L1 base fee Scalar
/// @param _blobBaseFeeScalar L1 blob base fee Scalar
/// @param _sequenceNumber Number of L2 blocks since epoch start.
/// @param _timestamp L1 timestamp.
/// @param _number L1 blocknumber.
/// @param _baseFee L1 base fee.
/// @param _blobBaseFee L1 blob base fee.
/// @param _hash L1 blockhash.
/// @param _batcherHash Versioned hash to authenticate batcher by.
function encodeSetL1BlockValuesEcotone(
uint32 _baseFeeScalar,
uint32 _blobBaseFeeScalar,
uint64 _sequenceNumber,
uint64 _timestamp,
uint64 _number,
uint256 _baseFee,
uint256 _blobBaseFee,
bytes32 _hash,
bytes32 _batcherHash
)
internal
pure
returns (bytes memory)
{
bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesEcotone()"));
return abi.encodePacked(
functionSignature,
_baseFeeScalar,
_blobBaseFeeScalar,
_sequenceNumber,
_timestamp,
_number,
_baseFee,
_blobBaseFee,
_hash,
_batcherHash
);
}
/// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesInterop
/// @param _baseFeeScalar L1 base fee Scalar
/// @param _blobBaseFeeScalar L1 blob base fee Scalar
/// @param _sequenceNumber Number of L2 blocks since epoch start.
/// @param _timestamp L1 timestamp.
/// @param _number L1 blocknumber.
/// @param _baseFee L1 base fee.
/// @param _blobBaseFee L1 blob base fee.
/// @param _hash L1 blockhash.
/// @param _batcherHash Versioned hash to authenticate batcher by.
function encodeSetL1BlockValuesInterop(
uint32 _baseFeeScalar,
uint32 _blobBaseFeeScalar,
uint64 _sequenceNumber,
uint64 _timestamp,
uint64 _number,
uint256 _baseFee,
uint256 _blobBaseFee,
bytes32 _hash,
bytes32 _batcherHash
)
internal
pure
returns (bytes memory)
{
bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesInterop()"));
return abi.encodePacked(
functionSignature,
_baseFeeScalar,
_blobBaseFeeScalar,
_sequenceNumber,
_timestamp,
_number,
_baseFee,
_blobBaseFee,
_hash,
_batcherHash
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries
import { Bytes } from "src/libraries/Bytes.sol";
import { RLPReader } from "src/libraries/rlp/RLPReader.sol";
/// @title MerkleTrie
/// @notice MerkleTrie is a small library for verifying standard Ethereum Merkle-Patricia trie
/// inclusion proofs. By default, this library assumes a hexary trie. One can change the
/// trie radix constant to support other trie radixes.
library MerkleTrie {
/// @notice Struct representing a node in the trie.
/// @custom:field encoded The RLP-encoded node.
/// @custom:field decoded The RLP-decoded node.
struct TrieNode {
bytes encoded;
RLPReader.RLPItem[] decoded;
}
/// @notice Determines the number of elements per branch node.
uint256 internal constant TREE_RADIX = 16;
/// @notice Branch nodes have TREE_RADIX elements and one value element.
uint256 internal constant BRANCH_NODE_LENGTH = TREE_RADIX + 1;
/// @notice Leaf nodes and extension nodes have two elements, a `path` and a `value`.
uint256 internal constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;
/// @notice Prefix for even-nibbled extension node paths.
uint8 internal constant PREFIX_EXTENSION_EVEN = 0;
/// @notice Prefix for odd-nibbled extension node paths.
uint8 internal constant PREFIX_EXTENSION_ODD = 1;
/// @notice Prefix for even-nibbled leaf node paths.
uint8 internal constant PREFIX_LEAF_EVEN = 2;
/// @notice Prefix for odd-nibbled leaf node paths.
uint8 internal constant PREFIX_LEAF_ODD = 3;
/// @notice Verifies a proof that a given key/value pair is present in the trie.
/// @param _key Key of the node to search for, as a hex string.
/// @param _value Value of the node to search for, as a hex string.
/// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle
/// trees, this proof is executed top-down and consists of a list of RLP-encoded
/// nodes that make a path down to the target node.
/// @param _root Known root of the Merkle trie. Used to verify that the included proof is
/// correctly constructed.
/// @return valid_ Whether or not the proof is valid.
function verifyInclusionProof(
bytes memory _key,
bytes memory _value,
bytes[] memory _proof,
bytes32 _root
)
internal
pure
returns (bool valid_)
{
valid_ = Bytes.equal(_value, get(_key, _proof, _root));
}
/// @notice Retrieves the value associated with a given key.
/// @param _key Key to search for, as hex bytes.
/// @param _proof Merkle trie inclusion proof for the key.
/// @param _root Known root of the Merkle trie.
/// @return value_ Value of the key if it exists.
function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) {
require(_key.length > 0, "MerkleTrie: empty key");
TrieNode[] memory proof = _parseProof(_proof);
bytes memory key = Bytes.toNibbles(_key);
bytes memory currentNodeID = abi.encodePacked(_root);
uint256 currentKeyIndex = 0;
// Proof is top-down, so we start at the first element (root).
for (uint256 i = 0; i < proof.length; i++) {
TrieNode memory currentNode = proof[i];
// Key index should never exceed total key length or we'll be out of bounds.
require(currentKeyIndex <= key.length, "MerkleTrie: key index exceeds total key length");
if (currentKeyIndex == 0) {
// First proof element is always the root node.
require(
Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID),
"MerkleTrie: invalid root hash"
);
} else if (currentNode.encoded.length >= 32) {
// Nodes 32 bytes or larger are hashed inside branch nodes.
require(
Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID),
"MerkleTrie: invalid large internal hash"
);
} else {
// Nodes smaller than 32 bytes aren't hashed.
require(Bytes.equal(currentNode.encoded, currentNodeID), "MerkleTrie: invalid internal node hash");
}
if (currentNode.decoded.length == BRANCH_NODE_LENGTH) {
if (currentKeyIndex == key.length) {
// Value is the last element of the decoded list (for branch nodes). There's
// some ambiguity in the Merkle trie specification because bytes(0) is a
// valid value to place into the trie, but for branch nodes bytes(0) can exist
// even when the value wasn't explicitly placed there. Geth treats a value of
// bytes(0) as "key does not exist" and so we do the same.
value_ = RLPReader.readBytes(currentNode.decoded[TREE_RADIX]);
require(value_.length > 0, "MerkleTrie: value length must be greater than zero (branch)");
// Extra proof elements are not allowed.
require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (branch)");
return value_;
} else {
// We're not at the end of the key yet.
// Figure out what the next node ID should be and continue.
uint8 branchKey = uint8(key[currentKeyIndex]);
RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey];
currentNodeID = _getNodeID(nextNode);
currentKeyIndex += 1;
}
} else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
bytes memory path = _getNodePath(currentNode);
uint8 prefix = uint8(path[0]);
uint8 offset = 2 - (prefix % 2);
bytes memory pathRemainder = Bytes.slice(path, offset);
bytes memory keyRemainder = Bytes.slice(key, currentKeyIndex);
uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder);
// Whether this is a leaf node or an extension node, the path remainder MUST be a
// prefix of the key remainder (or be equal to the key remainder) or the proof is
// considered invalid.
require(
pathRemainder.length == sharedNibbleLength,
"MerkleTrie: path remainder must share all nibbles with key"
);
if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
// Prefix of 2 or 3 means this is a leaf node. For the leaf node to be valid,
// the key remainder must be exactly equal to the path remainder. We already
// did the necessary byte comparison, so it's more efficient here to check that
// the key remainder length equals the shared nibble length, which implies
// equality with the path remainder (since we already did the same check with
// the path remainder and the shared nibble length).
require(
keyRemainder.length == sharedNibbleLength,
"MerkleTrie: key remainder must be identical to path remainder"
);
// Our Merkle Trie is designed specifically for the purposes of the Ethereum
// state trie. Empty values are not allowed in the state trie, so we can safely
// say that if the value is empty, the key should not exist and the proof is
// invalid.
value_ = RLPReader.readBytes(currentNode.decoded[1]);
require(value_.length > 0, "MerkleTrie: value length must be greater than zero (leaf)");
// Extra proof elements are not allowed.
require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (leaf)");
return value_;
} else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
// Prefix of 0 or 1 means this is an extension node. We move onto the next node
// in the proof and increment the key index by the length of the path remainder
// which is equal to the shared nibble length.
currentNodeID = _getNodeID(currentNode.decoded[1]);
currentKeyIndex += sharedNibbleLength;
} else {
revert("MerkleTrie: received a node with an unknown prefix");
}
} else {
revert("MerkleTrie: received an unparseable node");
}
}
revert("MerkleTrie: ran out of proof elements");
}
/// @notice Parses an array of proof elements into a new array that contains both the original
/// encoded element and the RLP-decoded element.
/// @param _proof Array of proof elements to parse.
/// @return proof_ Proof parsed into easily accessible structs.
function _parseProof(bytes[] memory _proof) private pure returns (TrieNode[] memory proof_) {
uint256 length = _proof.length;
proof_ = new TrieNode[](length);
for (uint256 i = 0; i < length;) {
proof_[i] = TrieNode({ encoded: _proof[i], decoded: RLPReader.readList(_proof[i]) });
unchecked {
++i;
}
}
}
/// @notice Picks out the ID for a node. Node ID is referred to as the "hash" within the
/// specification, but nodes < 32 bytes are not actually hashed.
/// @param _node Node to pull an ID for.
/// @return id_ ID for the node, depending on the size of its contents.
function _getNodeID(RLPReader.RLPItem memory _node) private pure returns (bytes memory id_) {
id_ = _node.length < 32 ? RLPReader.readRawBytes(_node) : RLPReader.readBytes(_node);
}
/// @notice Gets the path for a leaf or extension node.
/// @param _node Node to get a path for.
/// @return nibbles_ Node path, converted to an array of nibbles.
function _getNodePath(TrieNode memory _node) private pure returns (bytes memory nibbles_) {
nibbles_ = Bytes.toNibbles(RLPReader.readBytes(_node.decoded[0]));
}
/// @notice Utility; determines the number of nibbles shared between two nibble arrays.
/// @param _a First nibble array.
/// @param _b Second nibble array.
/// @return shared_ Number of shared nibbles.
function _getSharedNibbleLength(bytes memory _a, bytes memory _b) private pure returns (uint256 shared_) {
uint256 max = (_a.length < _b.length) ? _a.length : _b.length;
for (; shared_ < max && _a[shared_] == _b[shared_];) {
unchecked {
++shared_;
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for RLP encoding and CREATE address computation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibRLP.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol)
library LibRLP {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A pointer to a RLP item list in memory.
struct List {
// Do NOT modify the `_data` directly.
uint256 _data;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CREATE ADDRESS PREDICTION */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the address where a contract will be stored if deployed via
/// `deployer` with `nonce` using the `CREATE` opcode.
/// For the specification of the Recursive Length Prefix (RLP)
/// encoding scheme, please refer to p. 19 of the Ethereum Yellow Paper
/// (https://ethereum.github.io/yellowpaper/paper.pdf)
/// and the Ethereum Wiki (https://eth.wiki/fundamentals/rlp).
///
/// Based on the EIP-161 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md)
/// specification, all contract accounts on the Ethereum mainnet are initiated with
/// `nonce = 1`. Thus, the first contract address created by another contract
/// is calculated with a non-zero nonce.
///
/// The theoretical allowed limit, based on EIP-2681
/// (https://eips.ethereum.org/EIPS/eip-2681), for an account nonce is 2**64-2.
///
/// Caution! This function will NOT check that the nonce is within the theoretical range.
/// This is for performance, as exceeding the range is extremely impractical.
/// It is the user's responsibility to ensure that the nonce is valid
/// (e.g. no dirty bits after packing / unpacking).
///
/// This is equivalent to:
/// `address(uint160(uint256(keccak256(LibRLP.p(deployer).p(nonce).encode()))))`.
///
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function computeAddress(address deployer, uint256 nonce)
internal
pure
returns (address deployed)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
// The integer zero is treated as an empty byte string,
// and as a result it only has a length prefix, 0x80,
// computed via `0x80 + 0`.
// A one-byte integer in the [0x00, 0x7f] range uses its
// own value as a length prefix,
// there is no additional `0x80 + length` prefix that precedes it.
if iszero(gt(nonce, 0x7f)) {
mstore(0x00, deployer)
// Using `mstore8` instead of `or` naturally cleans
// any dirty upper bits of `deployer`.
mstore8(0x0b, 0x94)
mstore8(0x0a, 0xd6)
// `shl` 7 is equivalent to multiplying by 0x80.
mstore8(0x20, or(shl(7, iszero(nonce)), nonce))
deployed := keccak256(0x0a, 0x17)
break
}
let i := 8
// Just use a loop to generalize all the way with minimal bytecode size.
for {} shr(i, nonce) { i := add(i, 8) } {}
// `shr` 3 is equivalent to dividing by 8.
i := shr(3, i)
// Store in descending slot sequence to overlap the values correctly.
mstore(i, nonce)
mstore(0x00, shl(8, deployer))
mstore8(0x1f, add(0x80, i))
mstore8(0x0a, 0x94)
mstore8(0x09, add(0xd6, i))
deployed := keccak256(0x09, add(0x17, i))
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RLP ENCODING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note:
// - addresses are treated like byte strings of length 20, agnostic of leading zero bytes.
// - uint256s are converted to byte strings, stripped of leading zero bytes, and encoded.
// - bools are converted to uint256s (`b ? 1 : 0`), then encoded with the uint256.
// - For bytes1 to bytes32, you must manually convert them to bytes memory
// with `abi.encodePacked(x)` before encoding.
/// @dev Returns a new empty list.
function p() internal pure returns (List memory result) {}
/// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
function p(uint256 x) internal pure returns (List memory result) {
p(result, x);
}
/// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
function p(address x) internal pure returns (List memory result) {
p(result, x);
}
/// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
function p(bool x) internal pure returns (List memory result) {
p(result, x);
}
/// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
function p(bytes memory x) internal pure returns (List memory result) {
p(result, x);
}
/// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
function p(List memory x) internal pure returns (List memory result) {
p(result, x);
}
/// @dev Appends `x` to `list`. Returns `list` for function chaining.
function p(List memory list, uint256 x) internal pure returns (List memory result) {
result._data = x << 48;
_updateTail(list, result);
/// @solidity memory-safe-assembly
assembly {
// If `x` is too big, we cannot pack it inline with the node.
// We'll have to allocate a new slot for `x` and store the pointer to it in the node.
if shr(208, x) {
let m := mload(0x40)
mstore(m, x)
mstore(0x40, add(m, 0x20))
mstore(result, shl(40, or(1, shl(8, m))))
}
}
result = list;
}
/// @dev Appends `x` to `list`. Returns `list` for function chaining.
function p(List memory list, address x) internal pure returns (List memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(result, shl(40, or(4, shl(8, x))))
}
_updateTail(list, result);
result = list;
}
/// @dev Appends `x` to `list`. Returns `list` for function chaining.
function p(List memory list, bool x) internal pure returns (List memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(result, shl(48, iszero(iszero(x))))
}
_updateTail(list, result);
result = list;
}
/// @dev Appends `x` to `list`. Returns `list` for function chaining.
function p(List memory list, bytes memory x) internal pure returns (List memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(result, shl(40, or(2, shl(8, x))))
}
_updateTail(list, result);
result = list;
}
/// @dev Appends `x` to `list`. Returns `list` for function chaining.
function p(List memory list, List memory x) internal pure returns (List memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(result, shl(40, or(3, shl(8, x))))
}
_updateTail(list, result);
result = list;
}
/// @dev Returns the RLP encoding of `list`.
function encode(List memory list) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
function encodeUint(x_, o_) -> _o {
_o := add(o_, 1)
if iszero(gt(x_, 0x7f)) {
mstore8(o_, or(shl(7, iszero(x_)), x_)) // Copy `x_`.
leave
}
let r_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x_))
r_ := or(r_, shl(6, lt(0xffffffffffffffff, shr(r_, x_))))
r_ := or(r_, shl(5, lt(0xffffffff, shr(r_, x_))))
r_ := or(r_, shl(4, lt(0xffff, shr(r_, x_))))
r_ := or(shr(3, r_), lt(0xff, shr(r_, x_)))
mstore8(o_, add(r_, 0x81)) // Store the prefix.
mstore(0x00, x_)
mstore(_o, mload(xor(31, r_))) // Copy `x_`.
_o := add(add(1, r_), _o)
}
function encodeAddress(x_, o_) -> _o {
_o := add(o_, 0x15)
mstore(o_, shl(88, x_))
mstore8(o_, 0x94)
}
function encodeBytes(x_, o_, c_) -> _o {
_o := add(o_, 1)
let n_ := mload(x_)
if iszero(gt(n_, 55)) {
let f_ := mload(add(0x20, x_))
if iszero(and(eq(1, n_), lt(byte(0, f_), 0x80))) {
mstore8(o_, add(n_, c_)) // Store the prefix.
mstore(add(0x21, o_), mload(add(0x40, x_)))
mstore(_o, f_)
_o := add(n_, _o)
leave
}
mstore(o_, f_) // Copy `x_`.
leave
}
returndatacopy(returndatasize(), returndatasize(), shr(32, n_))
let r_ := add(1, add(lt(0xff, n_), add(lt(0xffff, n_), lt(0xffffff, n_))))
mstore(o_, shl(248, add(r_, add(c_, 55)))) // Store the prefix.
// Copy `x`.
let i_ := add(r_, _o)
_o := add(i_, n_)
for { let d_ := sub(add(0x20, x_), i_) } 1 {} {
mstore(i_, mload(add(d_, i_)))
i_ := add(i_, 0x20)
if iszero(lt(i_, _o)) { break }
}
mstore(o_, or(mload(o_), shl(sub(248, shl(3, r_)), n_))) // Store the prefix.
}
function encodeList(l_, o_) -> _o {
if iszero(mload(l_)) {
mstore8(o_, 0xc0)
_o := add(o_, 1)
leave
}
let j_ := add(o_, 0x20)
for { let h_ := l_ } 1 {} {
h_ := and(mload(h_), 0xffffffffff)
if iszero(h_) { break }
let t_ := byte(26, mload(h_))
if iszero(gt(t_, 1)) {
if iszero(t_) {
j_ := encodeUint(shr(48, mload(h_)), j_)
continue
}
j_ := encodeUint(mload(shr(48, mload(h_))), j_)
continue
}
if eq(t_, 2) {
j_ := encodeBytes(shr(48, mload(h_)), j_, 0x80)
continue
}
if eq(t_, 3) {
j_ := encodeList(shr(48, mload(h_)), j_)
continue
}
j_ := encodeAddress(shr(48, mload(h_)), j_)
}
let n_ := sub(j_, add(o_, 0x20))
if iszero(gt(n_, 55)) {
mstore8(o_, add(n_, 0xc0)) // Store the prefix.
mstore(add(0x01, o_), mload(add(0x20, o_)))
mstore(add(0x21, o_), mload(add(0x40, o_)))
_o := add(n_, add(0x01, o_))
leave
}
mstore(o_, n_)
_o := encodeBytes(o_, o_, 0xc0)
}
result := mload(0x40)
let begin := add(result, 0x20)
let end := encodeList(list, begin)
mstore(result, sub(end, begin)) // Store the length of `result`.
mstore(end, 0) // Zeroize the slot after `result`.
mstore(0x40, add(end, 0x20)) // Allocate memory for `result`.
}
}
/// @dev Returns the RLP encoding of `x`.
function encode(uint256 x) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := mload(0x40)
if iszero(gt(x, 0x7f)) {
mstore(result, 1) // Store the length of `result`.
mstore(add(result, 0x20), shl(248, or(shl(7, iszero(x)), x))) // Copy `x`.
mstore(0x40, add(result, 0x40)) // Allocate memory for `result`.
break
}
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := add(2, or(shr(3, r), lt(0xff, shr(r, x))))
mstore(add(r, result), x) // Copy `x`.
mstore(add(result, 1), add(r, 0x7f)) // Store the prefix.
mstore(result, r) // Store the length of `result`.
mstore(add(r, add(result, 0x20)), 0) // Zeroize the slot after `result`.
mstore(0x40, add(result, 0x60)) // Allocate memory for `result`.
break
}
}
}
/// @dev Returns the RLP encoding of `x`.
function encode(address x) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(result, 0x15)
let o := add(0x20, result)
mstore(o, shl(88, x))
mstore8(o, 0x94)
mstore(0x40, add(0x20, o))
}
}
/// @dev Returns the RLP encoding of `x`.
function encode(bool x) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(result, 1)
mstore(add(0x20, result), shl(add(0xf8, mul(7, iszero(x))), 0x01))
mstore(0x40, add(0x40, result))
}
}
/// @dev Returns the RLP encoding of `x`.
function encode(bytes memory x) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := x
for {} iszero(and(eq(1, mload(x)), lt(byte(0, mload(add(x, 0x20))), 0x80))) {} {
result := mload(0x40)
let n := mload(x) // Length of `x`.
if iszero(gt(n, 55)) {
mstore(0x40, add(result, 0x60))
mstore(add(0x41, result), mload(add(0x40, x)))
mstore(add(0x21, result), mload(add(0x20, x)))
mstore(add(1, result), add(n, 0x80)) // Store the prefix.
mstore(result, add(1, n)) // Store the length of `result`.
mstore(add(add(result, 0x21), n), 0) // Zeroize the slot after `result`.
break
}
returndatacopy(returndatasize(), returndatasize(), shr(32, n))
let r := add(2, add(lt(0xff, n), add(lt(0xffff, n), lt(0xffffff, n))))
// Copy `x`.
let i := add(r, add(0x20, result))
let end := add(i, n)
for { let d := sub(add(0x20, x), i) } 1 {} {
mstore(i, mload(add(d, i)))
i := add(i, 0x20)
if iszero(lt(i, end)) { break }
}
mstore(add(r, result), n) // Store the prefix.
mstore(add(1, result), add(r, 0xb6)) // Store the prefix.
mstore(result, add(r, n)) // Store the length of `result`.
mstore(end, 0) // Zeroize the slot after `result`.
mstore(0x40, add(end, 0x20)) // Allocate memory.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the tail in `list`.
function _updateTail(List memory list, List memory result) private pure {
/// @solidity memory-safe-assembly
assembly {
let v := or(shr(mload(list), result), mload(list))
let tail := shr(40, v)
mstore(list, xor(shl(40, xor(tail, result)), v)) // Update the tail.
mstore(tail, or(mload(tail), result)) // Make the previous tail point to `result`.
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2019-2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
library AddressAliasHelper {
uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
/// @notice Utility function that converts the address in the L1 that submitted a tx to
/// the inbox to the msg.sender viewed in the L2
/// @param l1Address the address in the L1 that triggered the tx to L2
/// @return l2Address L2 address as viewed in msg.sender
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + offset);
}
}
/// @notice Utility function that converts the msg.sender viewed in the L2 to the
/// address in the L1 that submitted a tx to the inbox
/// @param l2Address L2 address as viewed in msg.sender
/// @return l1Address the address in the L1 that triggered the tx to L2
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - offset);
}
}
}// 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.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt8(int256 x) internal pure returns (int8) {
int8 y = int8(x);
if (x != y) _revertOverflow();
return y;
}
function toInt16(int256 x) internal pure returns (int16) {
int16 y = int16(x);
if (x != y) _revertOverflow();
return y;
}
function toInt24(int256 x) internal pure returns (int24) {
int24 y = int24(x);
if (x != y) _revertOverflow();
return y;
}
function toInt32(int256 x) internal pure returns (int32) {
int32 y = int32(x);
if (x != y) _revertOverflow();
return y;
}
function toInt40(int256 x) internal pure returns (int40) {
int40 y = int40(x);
if (x != y) _revertOverflow();
return y;
}
function toInt48(int256 x) internal pure returns (int48) {
int48 y = int48(x);
if (x != y) _revertOverflow();
return y;
}
function toInt56(int256 x) internal pure returns (int56) {
int56 y = int56(x);
if (x != y) _revertOverflow();
return y;
}
function toInt64(int256 x) internal pure returns (int64) {
int64 y = int64(x);
if (x != y) _revertOverflow();
return y;
}
function toInt72(int256 x) internal pure returns (int72) {
int72 y = int72(x);
if (x != y) _revertOverflow();
return y;
}
function toInt80(int256 x) internal pure returns (int80) {
int80 y = int80(x);
if (x != y) _revertOverflow();
return y;
}
function toInt88(int256 x) internal pure returns (int88) {
int88 y = int88(x);
if (x != y) _revertOverflow();
return y;
}
function toInt96(int256 x) internal pure returns (int96) {
int96 y = int96(x);
if (x != y) _revertOverflow();
return y;
}
function toInt104(int256 x) internal pure returns (int104) {
int104 y = int104(x);
if (x != y) _revertOverflow();
return y;
}
function toInt112(int256 x) internal pure returns (int112) {
int112 y = int112(x);
if (x != y) _revertOverflow();
return y;
}
function toInt120(int256 x) internal pure returns (int120) {
int120 y = int120(x);
if (x != y) _revertOverflow();
return y;
}
function toInt128(int256 x) internal pure returns (int128) {
int128 y = int128(x);
if (x != y) _revertOverflow();
return y;
}
function toInt136(int256 x) internal pure returns (int136) {
int136 y = int136(x);
if (x != y) _revertOverflow();
return y;
}
function toInt144(int256 x) internal pure returns (int144) {
int144 y = int144(x);
if (x != y) _revertOverflow();
return y;
}
function toInt152(int256 x) internal pure returns (int152) {
int152 y = int152(x);
if (x != y) _revertOverflow();
return y;
}
function toInt160(int256 x) internal pure returns (int160) {
int160 y = int160(x);
if (x != y) _revertOverflow();
return y;
}
function toInt168(int256 x) internal pure returns (int168) {
int168 y = int168(x);
if (x != y) _revertOverflow();
return y;
}
function toInt176(int256 x) internal pure returns (int176) {
int176 y = int176(x);
if (x != y) _revertOverflow();
return y;
}
function toInt184(int256 x) internal pure returns (int184) {
int184 y = int184(x);
if (x != y) _revertOverflow();
return y;
}
function toInt192(int256 x) internal pure returns (int192) {
int192 y = int192(x);
if (x != y) _revertOverflow();
return y;
}
function toInt200(int256 x) internal pure returns (int200) {
int200 y = int200(x);
if (x != y) _revertOverflow();
return y;
}
function toInt208(int256 x) internal pure returns (int208) {
int208 y = int208(x);
if (x != y) _revertOverflow();
return y;
}
function toInt216(int256 x) internal pure returns (int216) {
int216 y = int216(x);
if (x != y) _revertOverflow();
return y;
}
function toInt224(int256 x) internal pure returns (int224) {
int224 y = int224(x);
if (x != y) _revertOverflow();
return y;
}
function toInt232(int256 x) internal pure returns (int232) {
int232 y = int232(x);
if (x != y) _revertOverflow();
return y;
}
function toInt240(int256 x) internal pure returns (int240) {
int240 y = int240(x);
if (x != y) _revertOverflow();
return y;
}
function toInt248(int256 x) internal pure returns (int248) {
int248 y = int248(x);
if (x != y) _revertOverflow();
return y;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt256(uint256 x) internal pure returns (int256) {
if (x >= 1 << 255) _revertOverflow();
return int256(x);
}
function toUint256(int256 x) internal pure returns (uint256) {
if (x < 0) _revertOverflow();
return uint256(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:attribution https://github.com/bakaoh/solidity-rlp-encode
/// @title RLPWriter
/// @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's
/// RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with minor
/// modifications to improve legibility.
library RLPWriter {
/// @notice RLP encodes a byte string.
/// @param _in The byte string to encode.
/// @return out_ The RLP encoded string in bytes.
function writeBytes(bytes memory _in) internal pure returns (bytes memory out_) {
if (_in.length == 1 && uint8(_in[0]) < 128) {
out_ = _in;
} else {
out_ = abi.encodePacked(_writeLength(_in.length, 128), _in);
}
}
/// @notice RLP encodes a list of RLP encoded byte byte strings.
/// @param _in The list of RLP encoded byte strings.
/// @return list_ The RLP encoded list of items in bytes.
function writeList(bytes[] memory _in) internal pure returns (bytes memory list_) {
list_ = _flatten(_in);
list_ = abi.encodePacked(_writeLength(list_.length, 192), list_);
}
/// @notice RLP encodes a string.
/// @param _in The string to encode.
/// @return out_ The RLP encoded string in bytes.
function writeString(string memory _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(bytes(_in));
}
/// @notice RLP encodes an address.
/// @param _in The address to encode.
/// @return out_ The RLP encoded address in bytes.
function writeAddress(address _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(abi.encodePacked(_in));
}
/// @notice RLP encodes a uint.
/// @param _in The uint256 to encode.
/// @return out_ The RLP encoded uint256 in bytes.
function writeUint(uint256 _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(_toBinary(_in));
}
/// @notice RLP encodes a bool.
/// @param _in The bool to encode.
/// @return out_ The RLP encoded bool in bytes.
function writeBool(bool _in) internal pure returns (bytes memory out_) {
out_ = new bytes(1);
out_[0] = (_in ? bytes1(0x01) : bytes1(0x80));
}
/// @notice Encode the first byte and then the `len` in binary form if `length` is more than 55.
/// @param _len The length of the string or the payload.
/// @param _offset 128 if item is string, 192 if item is list.
/// @return out_ RLP encoded bytes.
function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory out_) {
if (_len < 56) {
out_ = new bytes(1);
out_[0] = bytes1(uint8(_len) + uint8(_offset));
} else {
uint256 lenLen;
uint256 i = 1;
while (_len / i != 0) {
lenLen++;
i *= 256;
}
out_ = new bytes(lenLen + 1);
out_[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55);
for (i = 1; i <= lenLen; i++) {
out_[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256));
}
}
}
/// @notice Encode integer in big endian binary form with no leading zeroes.
/// @param _x The integer to encode.
/// @return out_ RLP encoded bytes.
function _toBinary(uint256 _x) private pure returns (bytes memory out_) {
bytes memory b = abi.encodePacked(_x);
uint256 i = 0;
for (; i < 32; i++) {
if (b[i] != 0) {
break;
}
}
out_ = new bytes(32 - i);
for (uint256 j = 0; j < out_.length; j++) {
out_[j] = b[i++];
}
}
/// @custom:attribution https://github.com/Arachnid/solidity-stringutils
/// @notice Copies a piece of memory to another location.
/// @param _dest Destination location.
/// @param _src Source location.
/// @param _len Length of memory to copy.
function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure {
uint256 dest = _dest;
uint256 src = _src;
uint256 len = _len;
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
uint256 mask;
unchecked {
mask = 256 ** (32 - len) - 1;
}
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/// @custom:attribution https://github.com/sammayo/solidity-rlp-encoder
/// @notice Flattens a list of byte strings into one byte string.
/// @param _list List of byte strings to flatten.
/// @return out_ The flattened byte string.
function _flatten(bytes[] memory _list) private pure returns (bytes memory out_) {
if (_list.length == 0) {
return new bytes(0);
}
uint256 len;
uint256 i = 0;
for (; i < _list.length; i++) {
len += _list[i].length;
}
out_ = new bytes(len);
uint256 flattenedPtr;
assembly {
flattenedPtr := add(out_, 0x20)
}
for (i = 0; i < _list.length; i++) {
bytes memory item = _list[i];
uint256 listPtr;
assembly {
listPtr := add(item, 0x20)
}
_memcpy(flattenedPtr, listPtr, item.length);
flattenedPtr += _list[i].length;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Bytes
/// @notice Bytes is a library for manipulating byte arrays.
library Bytes {
/// @custom:attribution https://github.com/GNSPS/solidity-bytes-utils
/// @notice Slices a byte array with a given starting index and length. Returns a new byte array
/// as opposed to a pointer to the original array. Will throw if trying to slice more
/// bytes than exist in the array.
/// @param _bytes Byte array to slice.
/// @param _start Starting index of the slice.
/// @param _length Length of the slice.
/// @return Slice of the input byte array.
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
unchecked {
require(_length + 31 >= _length, "slice_overflow");
require(_start + _length >= _start, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
}
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} { mstore(mc, mload(cc)) }
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
/// @notice Slices a byte array with a given starting index up to the end of the original byte
/// array. Returns a new array rathern than a pointer to the original.
/// @param _bytes Byte array to slice.
/// @param _start Starting index of the slice.
/// @return Slice of the input byte array.
function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) {
if (_start >= _bytes.length) {
return bytes("");
}
return slice(_bytes, _start, _bytes.length - _start);
}
/// @notice Converts a byte array into a nibble array by splitting each byte into two nibbles.
/// Resulting nibble array will be exactly twice as long as the input byte array.
/// @param _bytes Input byte array to convert.
/// @return Resulting nibble array.
function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
bytes memory _nibbles;
assembly {
// Grab a free memory offset for the new array
_nibbles := mload(0x40)
// Load the length of the passed bytes array from memory
let bytesLength := mload(_bytes)
// Calculate the length of the new nibble array
// This is the length of the input array times 2
let nibblesLength := shl(0x01, bytesLength)
// Update the free memory pointer to allocate memory for the new array.
// To do this, we add the length of the new array + 32 bytes for the array length
// rounded up to the nearest 32 byte boundary to the current free memory pointer.
mstore(0x40, add(_nibbles, and(not(0x1F), add(nibblesLength, 0x3F))))
// Store the length of the new array in memory
mstore(_nibbles, nibblesLength)
// Store the memory offset of the _bytes array's contents on the stack
let bytesStart := add(_bytes, 0x20)
// Store the memory offset of the nibbles array's contents on the stack
let nibblesStart := add(_nibbles, 0x20)
// Loop through each byte in the input array
for { let i := 0x00 } lt(i, bytesLength) { i := add(i, 0x01) } {
// Get the starting offset of the next 2 bytes in the nibbles array
let offset := add(nibblesStart, shl(0x01, i))
// Load the byte at the current index within the `_bytes` array
let b := byte(0x00, mload(add(bytesStart, i)))
// Pull out the first nibble and store it in the new array
mstore8(offset, shr(0x04, b))
// Pull out the second nibble and store it in the new array
mstore8(add(offset, 0x01), and(b, 0x0F))
}
}
return _nibbles;
}
/// @notice Compares two byte arrays by comparing their keccak256 hashes.
/// @param _bytes First byte array to compare.
/// @param _other Second byte array to compare.
/// @return True if the two byte arrays are equal, false otherwise.
function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) {
return keccak256(_bytes) == keccak256(_other);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
// Libraries
import {
EmptyItem,
UnexpectedString,
InvalidDataRemainder,
ContentLengthMismatch,
InvalidHeader,
UnexpectedList
} from "src/libraries/rlp/RLPErrors.sol";
/// @custom:attribution https://github.com/hamdiallam/Solidity-RLP
/// @title RLPReader
/// @notice RLPReader is a library for parsing RLP-encoded byte arrays into Solidity types. Adapted
/// from Solidity-RLP (https://github.com/hamdiallam/Solidity-RLP) by Hamdi Allam with
/// various tweaks to improve readability.
library RLPReader {
/// @notice Custom pointer type to avoid confusion between pointers and uint256s.
type MemoryPointer is uint256;
/// @notice RLP item types.
/// @custom:value DATA_ITEM Represents an RLP data item (NOT a list).
/// @custom:value LIST_ITEM Represents an RLP list item.
enum RLPItemType {
DATA_ITEM,
LIST_ITEM
}
/// @notice Struct representing an RLP item.
/// @custom:field length Length of the RLP item.
/// @custom:field ptr Pointer to the RLP item in memory.
struct RLPItem {
uint256 length;
MemoryPointer ptr;
}
/// @notice Max list length that this library will accept.
uint256 internal constant MAX_LIST_LENGTH = 32;
/// @notice Converts bytes to a reference to memory position and length.
/// @param _in Input bytes to convert.
/// @return out_ Output memory reference.
function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory out_) {
// Empty arrays are not RLP items.
if (_in.length == 0) revert EmptyItem();
MemoryPointer ptr;
assembly {
ptr := add(_in, 32)
}
out_ = RLPItem({ length: _in.length, ptr: ptr });
}
/// @notice Reads an RLP list value into a list of RLP items.
/// @param _in RLP list value.
/// @return out_ Decoded RLP list items.
function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory out_) {
(uint256 listOffset, uint256 listLength, RLPItemType itemType) = _decodeLength(_in);
if (itemType != RLPItemType.LIST_ITEM) revert UnexpectedString();
if (listOffset + listLength != _in.length) revert InvalidDataRemainder();
// Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
// writing to the length. Since we can't know the number of RLP items without looping over
// the entire input, we'd have to loop twice to accurately size this array. It's easier to
// simply set a reasonable maximum list length and decrease the size before we finish.
out_ = new RLPItem[](MAX_LIST_LENGTH);
uint256 itemCount = 0;
uint256 offset = listOffset;
while (offset < _in.length) {
(uint256 itemOffset, uint256 itemLength,) = _decodeLength(
RLPItem({ length: _in.length - offset, ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset) })
);
// We don't need to check itemCount < out.length explicitly because Solidity already
// handles this check on our behalf, we'd just be wasting gas.
out_[itemCount] = RLPItem({
length: itemLength + itemOffset,
ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset)
});
itemCount += 1;
offset += itemOffset + itemLength;
}
// Decrease the array size to match the actual item count.
assembly {
mstore(out_, itemCount)
}
}
/// @notice Reads an RLP list value into a list of RLP items.
/// @param _in RLP list value.
/// @return out_ Decoded RLP list items.
function readList(bytes memory _in) internal pure returns (RLPItem[] memory out_) {
out_ = readList(toRLPItem(_in));
}
/// @notice Reads an RLP bytes value into bytes.
/// @param _in RLP bytes value.
/// @return out_ Decoded bytes.
function readBytes(RLPItem memory _in) internal pure returns (bytes memory out_) {
(uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);
if (itemType != RLPItemType.DATA_ITEM) revert UnexpectedList();
if (_in.length != itemOffset + itemLength) revert InvalidDataRemainder();
out_ = _copy(_in.ptr, itemOffset, itemLength);
}
/// @notice Reads an RLP bytes value into bytes.
/// @param _in RLP bytes value.
/// @return out_ Decoded bytes.
function readBytes(bytes memory _in) internal pure returns (bytes memory out_) {
out_ = readBytes(toRLPItem(_in));
}
/// @notice Reads the raw bytes of an RLP item.
/// @param _in RLP item to read.
/// @return out_ Raw RLP bytes.
function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory out_) {
out_ = _copy(_in.ptr, 0, _in.length);
}
/// @notice Decodes the length of an RLP item.
/// @param _in RLP item to decode.
/// @return offset_ Offset of the encoded data.
/// @return length_ Length of the encoded data.
/// @return type_ RLP item type (LIST_ITEM or DATA_ITEM).
function _decodeLength(RLPItem memory _in)
private
pure
returns (uint256 offset_, uint256 length_, RLPItemType type_)
{
// Short-circuit if there's nothing to decode, note that we perform this check when
// the user creates an RLP item via toRLPItem, but it's always possible for them to bypass
// that function and create an RLP item directly. So we need to check this anyway.
if (_in.length == 0) revert EmptyItem();
MemoryPointer ptr = _in.ptr;
uint256 prefix;
assembly {
prefix := byte(0, mload(ptr))
}
if (prefix <= 0x7f) {
// Single byte.
return (0, 1, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xb7) {
// Short string.
// slither-disable-next-line variable-scope
uint256 strLen = prefix - 0x80;
if (_in.length <= strLen) revert ContentLengthMismatch();
bytes1 firstByteOfContent;
assembly {
firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
}
if (strLen == 1 && firstByteOfContent < 0x80) revert InvalidHeader();
return (1, strLen, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xbf) {
// Long string.
uint256 lenOfStrLen = prefix - 0xb7;
if (_in.length <= lenOfStrLen) revert ContentLengthMismatch();
bytes1 firstByteOfContent;
assembly {
firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
}
if (firstByteOfContent == 0x00) revert InvalidHeader();
uint256 strLen;
assembly {
strLen := shr(sub(256, mul(8, lenOfStrLen)), mload(add(ptr, 1)))
}
if (strLen <= 55) revert InvalidHeader();
if (_in.length <= lenOfStrLen + strLen) revert ContentLengthMismatch();
return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xf7) {
// Short list.
// slither-disable-next-line variable-scope
uint256 listLen = prefix - 0xc0;
if (_in.length <= listLen) revert ContentLengthMismatch();
return (1, listLen, RLPItemType.LIST_ITEM);
} else {
// Long list.
uint256 lenOfListLen = prefix - 0xf7;
if (_in.length <= lenOfListLen) revert ContentLengthMismatch();
bytes1 firstByteOfContent;
assembly {
firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
}
if (firstByteOfContent == 0x00) revert InvalidHeader();
uint256 listLen;
assembly {
listLen := shr(sub(256, mul(8, lenOfListLen)), mload(add(ptr, 1)))
}
if (listLen <= 55) revert InvalidHeader();
if (_in.length <= lenOfListLen + listLen) revert ContentLengthMismatch();
return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
}
}
/// @notice Copies the bytes from a memory location.
/// @param _src Pointer to the location to read from.
/// @param _offset Offset to start reading from.
/// @param _length Number of bytes to read.
/// @return out_ Copied bytes.
function _copy(MemoryPointer _src, uint256 _offset, uint256 _length) private pure returns (bytes memory out_) {
out_ = new bytes(_length);
if (_length == 0) {
return out_;
}
// Mostly based on Solidity's copy_memory_to_memory:
// https://github.com/ethereum/solidity/blob/34dd30d71b4da730488be72ff6af7083cf2a91f6/libsolidity/codegen/YulUtilFunctions.cpp#L102-L114
uint256 src = MemoryPointer.unwrap(_src) + _offset;
assembly {
let dest := add(out_, 32)
let i := 0
for { } lt(i, _length) { i := add(i, 32) } { mstore(add(dest, i), mload(add(src, i))) }
if gt(i, _length) { mstore(add(dest, _length), 0) }
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @notice The length of an RLP item must be greater than zero to be decodable error EmptyItem(); /// @notice The decoded item type for list is not a list item error UnexpectedString(); /// @notice The RLP item has an invalid data remainder error InvalidDataRemainder(); /// @notice Decoded item type for bytes is not a string item error UnexpectedList(); /// @notice The length of the content must be greater than the RLP item length error ContentLengthMismatch(); /// @notice Invalid RLP header for RLP item error InvalidHeader();
{
"remappings": [
"0xFacet/facet-sol-1.0.0/=dependencies/0xFacet-facet-sol-1.0.0/",
"@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/",
"interfaces/=lib/optimism/packages/contracts-bedrock/interfaces/",
"@lib-keccak/=lib/lib-keccak/contracts/lib/",
"src/cannon/libraries/=lib/optimism/packages/contracts-bedrock/src/cannon/libraries/",
"facet-sol/=lib/facet-sol/",
"optimism/=lib/optimism/",
"@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/",
"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": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract Rollup","name":"_rollup","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContentLengthMismatch","type":"error"},{"inputs":[],"name":"EmptyItem","type":"error"},{"inputs":[],"name":"InvalidDataRemainder","type":"error"},{"inputs":[],"name":"InvalidDepositAmount","type":"error"},{"inputs":[],"name":"InvalidDepositParameters","type":"error"},{"inputs":[],"name":"InvalidHeader","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOutputRoot","type":"error"},{"inputs":[],"name":"InvalidWithdrawalProof","type":"error"},{"inputs":[],"name":"L2BridgeAlreadySet","type":"error"},{"inputs":[],"name":"L2BridgeNotSet","type":"error"},{"inputs":[],"name":"OnlyCanDepositWithoutTo","type":"error"},{"inputs":[],"name":"ProposalNotCanonical","type":"error"},{"inputs":[],"name":"RootBlacklisted","type":"error"},{"inputs":[],"name":"UnexpectedList","type":"error"},{"inputs":[],"name":"UnexpectedString","type":"error"},{"inputs":[],"name":"WithdrawalAlreadyFinalized","type":"error"},{"inputs":[],"name":"WithdrawalAlreadyProven","type":"error"},{"inputs":[],"name":"WithdrawalDelayNotMet","type":"error"},{"inputs":[],"name":"WithdrawalNotProven","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositReplayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRollup","type":"address"},{"indexed":true,"internalType":"address","name":"newRollup","type":"address"}],"name":"RollupUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"RootBlacklistStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"WithdrawalDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"WithdrawalFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rollup","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"WithdrawalProven","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"finalizeWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"finalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"initiateDeposit","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"l2Bridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"proposalId","type":"uint256"},{"components":[{"internalType":"bytes32","name":"version","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"messagePasserStorageRoot","type":"bytes32"},{"internalType":"bytes32","name":"latestBlockhash","type":"bytes32"}],"internalType":"struct Types.OutputRootProof","name":"rootProof","type":"tuple"},{"internalType":"bytes[]","name":"withdrawalProof","type":"bytes[]"}],"name":"proveWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"contract Rollup","name":"","type":"address"}],"name":"proven","outputs":[{"internalType":"uint32","name":"proposalId","type":"uint32"},{"internalType":"uint32","name":"provenAt","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct L1Bridge.DepositTransaction","name":"deposit","type":"tuple"}],"name":"replayDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollup","outputs":[{"internalType":"contract Rollup","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"rootBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_l2Bridge","type":"address"}],"name":"setL2Bridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rollup","type":"address"}],"name":"setRollup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"setRootBlacklisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawalDelay","type":"uint256"}],"name":"setWithdrawalDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6080346100c257601f62002d6038819003918201601f19168301916001600160401b038311848410176100c6578084926020946040528339810103126100c257516001600160a01b039081811681036100c2575f543360018060a01b03198216175f55604051923391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360018055600280546001600160a81b03191660089290921b610100600160a81b0316919091179055612c859081620000db8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610022575b3615610018575f80fd5b6100206112ca565b005b5f3560e01c80630abea268146101815780633d36d9711461017c5780633f4ba83a146101775780635c975abb146101725780636897a52d1461016d578063715018a614610168578063763131a0146101635780638456cb591461015e5780638da5cb5b14610159578063a7ab696114610154578063abc0f4011461014f578063ae1f6aaf1461014a578063b8012cb514610145578063cb23bcb514610140578063cbc43bb91461013b578063d2c13da514610136578063de35f5cb14610131578063e6cd84bf1461012c578063e7a9ad5314610127578063eb12dd5614610122578063ec489d171461011d5763f2fde38b0361000e57610ac8565b610a4a565b6109ed565b610942565b61084f565b610832565b6107cd565b61075d565b610727565b6106fd565b6106d5565b61047c565b61045f565b610438565b6103df565b61035f565b610308565b6102d9565b6102b7565b610224565b6101c5565b346101b05760203660031901126101b0576004355f526007602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160a01b038116036101b057565b346101b05760203660031901126101b0576004356101e2816101b4565b6101ea6113b2565b600354906001600160a01b0390818316610212576001600160a01b0319909216911617600355005b60405163299fdc3960e01b8152600490fd5b346101b0575f3660031901126101b05761023c6113b2565b60025460ff81161561027b5760ff19166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346101b0575f3660031901126101b057602060ff600254166040519015158152f35b346101b05760203660031901126101b0576004355f526004602052602060ff60405f2054166040519015158152f35b346101b0575f3660031901126101b0576103206113b2565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101b05760203660031901126101b05760043561037c816101b4565b6103846113b2565b60028054610100600160a81b03198116600884811b610100600160a81b0316919091179092556001600160a01b0392831692911c167f68cd55ad97baa2df51330d7c27f1e5cffbb01f028c038157ec439e8e4ddfec075f80a3005b346101b0575f3660031901126101b0576103f76113b2565b6103ff611409565b600160ff1960025416176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346101b0575f3660031901126101b0575f546040516001600160a01b039091168152602090f35b346101b0575f3660031901126101b0576020600554604051908152f35b346101b05760603660031901126101b057600480359061049b826101b4565b60243590604435926002600154146106915760026001556104ba611409565b6104c584848361144d565b6104d7815f52600660205260405f2090565b60025460081c6001600160a01b03165f8181526020929092526040909120909390549363ffffffff808660201c16801561068057610527610520865f52600760205260405f2090565b5460ff1690565b61066f5760055461053791610bc7565b42111561065e576040516318feeb1560e31b8152951663ffffffff168286019081526001600160a01b039591610140918291849182908a9082906020010392165afa80156106595761059f92610520925f9261062c575b5050515f52600460205260405f2090565b61061d57507f7effe66fd2d9671e7fc0ed5f9e2ab55fec23fb2f726bc2dc15c02e661528672d93946105ec6105df610611935f52600760205260405f2090565b805460ff19166001179055565b6105f68684611515565b60405193849316958360209093929193604081019481520152565b0390a261002060018055565b604051638edd8c9560e01b8152fd5b61064b9250803d10610652575b6106438183610c59565b810190610cec565b5f8061058e565b503d610639565b610da3565b604051635ca831e160e11b81528390fd5b604051632ba2651560e21b81528490fd5b6040516367ba7fe160e11b81528490fd5b60405162461bcd60e51b8152602081840152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b346101b0575f3660031901126101b0576003546040516001600160a01b039091168152602090f35b346101b05760203660031901126101b0576004355f526008602052602060405f2054604051908152f35b346101b0575f3660031901126101b05760025460405160089190911c6001600160a01b03168152602090f35b801515036101b057565b346101b05760403660031901126101b0576004357f6e5a4355570a915cfb0aafb4210d86823e2f3484b7610255ace1bf5f28534a9260206024356107a081610753565b6107a86113b2565b835f526004825260405f209015159060ff1981541660ff8316179055604051908152a2005b346101b05760203660031901126101b0577f9c3f1b54b1487e018f1d0593ff5cf7fb625b2df6332c974a6cc56bb35887984160043561080a6113b2565b6005548160055561082d6040519283928360209093929193604081019481520152565b0390a1005b346101b0575f3660031901126101b0576020600954604051908152f35b60203660031901126101b057600435610867816101b4565b61086f611409565b6003546001600160a01b039081161561093057341561091e57602091610896600954610dae565b916108a083600955565b6108e36108ab610c9a565b8481526001600160a01b038416868201523460408201526108cb81611559565b6108dd865f52600860205260405f2090565b556115a6565b604051348152911690339083907f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4604051908152f35b60405163fe9ba5cd60e01b8152600490fd5b604051633322339360e11b8152600490fd5b346101b05760603660031901126101b05761095b611409565b600435805f52600860205260405f205461097c61097736610dbc565b611559565b036109db5761099261098d36610dbc565b6115a6565b6024359061099f826101b4565b60405160443581526001600160a01b03909216917f9ed601de5fce88cea72c2f1c91a4868d6144eaa63a5ea75453cbbceb0ef3929190602090a3005b60405163d8ca7f1760e01b8152600490fd5b346101b05760403660031901126101b0576040610a31602435610a0f816101b4565b6004355f526006602052825f209060018060a01b03165f5260205260405f2090565b5463ffffffff825191818116835260201c166020820152f35b346101b0576101203660031901126101b057600435610a68816101b4565b60803660831901126101b05761010435906001600160401b03908183116101b057366023840112156101b05782600401359182116101b0573660248360051b850101116101b0576024610020930190606435906044359060243590610e06565b346101b05760203660031901126101b057600435610ae5816101b4565b610aed6113b2565b6001600160a01b03908116908115610b3e575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b9060018201809211610bb457565b610b92565b6001019081600111610bb457565b91908201809211610bb457565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610c0357604052565b610bd4565b602081019081106001600160401b03821117610c0357604052565b606081019081106001600160401b03821117610c0357604052565b60a081019081106001600160401b03821117610c0357604052565b90601f801991011681019081106001600160401b03821117610c0357604052565b6040519061014082018281106001600160401b03821117610c0357604052565b60405190610ca782610c23565b565b60405190610ca782610be8565b5190610ca7826101b4565b519063ffffffff821682036101b057565b519060058210156101b057565b519060038210156101b057565b90816101409103126101b057610d00610c7a565b9080518252610d1160208201610cb6565b6020830152610d2260408201610cc1565b6040830152610d3360608201610cc1565b6060830152610d4460808201610cc1565b6080830152610d5560a08201610cc1565b60a0830152610d6660c08201610cd2565b60c0830152610d7760e08201610cdf565b60e0830152610100610d8a818301610cb6565b90830152610d9c610120809201610cb6565b9082015290565b6040513d5f823e3d90fd5b5f198114610bb45760010190565b60609060031901126101b05760405190606082018281106001600160401b03821117610c0357604052816004358152602435610df7816101b4565b60208201526040604435910152565b9194929094610e13611409565b610e1e81878561144d565b610e30815f52600660205260405f2090565b60025460081c6001600160a01b0316918291610e72610e6563ffffffff9586939060018060a01b03165f5260205260405f2090565b5460201c63ffffffff1690565b1661116057610e8c610520825f52600760205260405f2090565b61114e5760408051636338049b60e01b8152600480820188905291996001600160a01b039960209392909190868c16908581602481855afa908115610659575f91611121575b5015611111578c516318feeb1560e31b81528481018b81526101409283918391908290819060200103915afa918215610659575f926110f4575b5050610f2461052082515f52600460205260405f2090565b6110e45751610f3a610f353661118a565b61173c565b036110d457610fc191610f94610fbd92610fa08f80519089820182610f6c8d8360205f91939293604081019481520152565b0392610f80601f1994858101835282610c59565b51902090518a810191825294859160200190565b03908101845283610c59565b610fb7610fab6111ee565b9160c435943691611222565b91611777565b1590565b6110c65750926110456110c1959361103061107f946110227fb7f82ff0ef7dc798b8b68a2fb464607fae371501d4b0b87f4123f657dcbe20ce9c9b9a98611006610ca9565b63ffffffff8b89161681529642169087019063ffffffff169052565b5f52600660205260405f2090565b9060018060a01b03165f5260205260405f2090565b9063ffffffff81511667ffffffff0000000060208454938363ffffffff198616178655015160201b16916001600160401b03191617179055565b6002546110a5906110999060081c6001600160a01b031681565b6001600160a01b031690565b9651978852602088015260408701529116939081906060820190565b0390a3565b8951632e1aaf2760e21b8152fd5b8b516315ae1f5d60e31b81528390fd5b8c51638edd8c9560e01b81528490fd5b61110a9250803d10610652576106438183610c59565b5f80610f0c565b8c516307f310d160e41b81528490fd5b6111419150863d8811611147575b6111398183610c59565b810190611172565b5f610ed2565b503d61112f565b604051632ba2651560e21b8152600490fd5b60405163030f885f60e61b8152600490fd5b908160209103126101b0575161118781610753565b90565b60809060831901126101b05760405190608082018281106001600160401b03821117610c035760405281608435815260a435602082015260c4356040820152606060e435910152565b6001600160401b038111610c0357601f01601f191660200190565b604051906111fb82610be8565b60018252600160f81b6020830152565b6001600160401b038111610c035760051b60200190565b9291909261122f8461120b565b916040946112406040519485610c59565b839581855260208095019160051b8401938385116101b05780925b85841061126b5750505050505050565b83356001600160401b0381116101b057820185601f820112156101b057803591611294836111d3565b6112a086519182610c59565b838152878a85850101116101b0575f8a85819682809701838601378301015281520193019261125b565b3332036113855760015b15611373576112e1611409565b6003546001600160a01b03161561093057341561091e57611303600954610dae565b61130c81600955565b611341611317610c9a565b82815233602082015234604082015261132f81611559565b6108dd845f52600860205260405f2090565b604051348152339182917f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4565b60405163770d5e3960e11b8152600490fd5b333b6017036113ac5762ef01006040516020810160405260205f82333c5160e81c146112d4565b5f6112d4565b5f546001600160a01b031633036113c557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60ff6002541661141557565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b604091825191602083019160018060a01b0380921683528484015283835261147483610c23565b600354169280519260c08401938085106001600160401b03861117610c035760a0829161150f96855288815287602082015230858201525f60608201525f608082015201528151948592602084019788528301523060608301525f60808301525f60a083015260c080830152518060e08301526114f88161010094858501906117b7565b601f801991011681010360e0810184520182610c59565b51902090565b81471061154c575f38818085856108fcf11561152f575050565b601691600b915f526073825360ff602053f01561154857565b3838fd5b63b12d13eb5f526004601cfd5b805190604060018060a01b0360208301511691015160405191602083019384526040830152606082015260608152608081018181106001600160401b03821117610c035760405251902090565b604080519181602091636093e77160e01b838601528051602486015260018060a01b038382015116604486015201516064840152606483526115e783610c3e565b600354928251936bffffffffffffffffffffffff199060601b16828501526014845261161284610be8565b82519061161e82610c08565b5f8252466001036116d757620face79493926116846116d29361167d6021946116628a995b61165b8a519b6116528d610c08565b5f8d528c61284b565b508a6128a9565b5061166c896127e2565b5061167689612812565b50886128a9565b50866128a9565b508351946116c26116988388018093612ae8565b92601f198885030188525f8452830195868152830196602360f91b885251809260418501906117b7565b8101036001810184520182610c59565b5190a1565b4662aa36a70361170257620face79493926116846116d29361167d60219461166262face7a99611643565b835162461bcd60e51b8152600481018490526013602482015272155b9cdd5c1c1bdc9d19590818da185a5b9259606a1b6044820152606490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261150f81610c3e565b926117a3919261118794602081519101206040519060208201526020815261179e81610be8565b611d02565b906020815191012090602081519101201490565b5f5b8381106117c85750505f910152565b81810151838201526020016117b9565b156117df57565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b5f52603260045260245ffd5b8051600110156118405760400190565b61181c565b805160101015611840576102200190565b80518210156118405760209160051b010190565b1561187157565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b156118d457565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b1561192f57565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b1561198b57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b8051156118405760200190565b908151811015611840570160200190565b60ff166002039060ff8211610bb457565b15611a0657565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15611a7857565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15611aea57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b5f19810191908211610bb457565b60f619810191908211610bb457565b60bf19810191908211610bb457565b60b619810191908211610bb457565b607f19810191908211610bb457565b91908203918211610bb457565b15611bb357565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15611c2557565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15611c9757565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190611d1c611d2291611d17855115156117d8565b6120d9565b92612192565b90604080519081611d3b60209485830160209181520190565b0391611d4f601f1993848101835282610c59565b935f925f945b875186101561208757611d688689611856565b5196611d77845187111561186a565b8561203357611dc1611dc691611da2611dae8b51868151910120895192839188830160209181520190565b03878101835282610c59565b6020815191012090602081519101201490565b611984565b80870196875151601181145f14611e7257505082518503611e18575050505050611e12611dff611df96111879451611845565b51612365565b93611e0c85511515611c1e565b51611b55565b14611c90565b909192949395611e66611e60611e5a60019360ff611e50611e4a611e3c8e8c6119dd565b516001600160f81b03191690565b60f81c90565b9151911690611856565b51612323565b97610ba6565b945b0194929190611d55565b94929099989795939160028096145f14611fde57611e9360ff999a9b6121e6565b95611ea3611e4a611e3c896119d0565b96611ec4600191611ebe611eb8848c166119ee565b60ff1690565b90612206565b9a8b98611ee7611ede611ed78b8a612206565b809c6122bd565b809e51146119ff565b169182148015611fd4575b15611f305750505050505050611df9611f2a92611f1761118796611f1d945114611a71565b51611830565b93611e0c85511515611ae3565b14611bac565b90809295999893969a94975015918215611fca575b505015611f6b57600191611f5f611e5a611f659351611830565b98610bc7565b94611e68565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b1490505f80611f45565b5060038214611ef2565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b87518051831161206e575061206461206991611da2611dae8b51868151910120895192839188830160209181520190565b611928565b611dc6565b80516020918201208251919092012061206991146118cd565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b9081516120e58161120b565b60406120f46040519283610c59565b828252601f196121038461120b565b01905f5b82811061216f5750505080935f5b8381106121225750505050565b8061212f60019284611856565b5161214361213d8386611856565b516123c1565b61214b610ca9565b918252602082015261215d8286611856565b526121688185611856565b5001612115565b602090825161217d81610be8565b60608082528382015285820183015201612107565b6040519080516001918160011b603f8101601f1916850160405284525f5b8281106121be575050505090565b808491821b86016021600f60208085880101515f1a908160041c9085015316910153016121b0565b60200151805115611840576122016020611187920151612365565b612192565b9081518110156122a957815181810390808211610bb457829061222e83601f810110156124fc565b612249838301612240848210156124fc565b86511015612539565b03612261575050506040515f81526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106122965750508252601f01601f191660405290565b909283518152602080910193019061227e565b50506040516122b781610c08565b5f815290565b805182515f94939185918082101561231a575094505b5b848110806122f0575b156122ea576001016122d4565b93505050565b506001600160f81b03198061230583866119dd565b51169061231283856119dd565b5116146122dd565b905094506122d3565b60208151105f1461233e5760208101519051611187916126f3565b61118790612365565b6002111561235157565b634e487b7160e01b5f52602160045260245ffd5b61236e81612579565b6002819392931015612351576123af57825182820190818311610bb4570361239d576020611187930151612769565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6123c9612bed565b508051156124ea576020815191604051926123e384610be8565b83526020808401910181526123f783612579565b91939061240383612347565b60018093036124d8576124169085610bc7565b85510361239d57612425612c05565b945f94835b612439575b5050505050815290565b8151808210156124d2578161244d91611b9f565b8186519061245a91610bc7565b612462610ca9565b9182528482015261247290612579565b509661247e8289610bc7565b8388519061248b91610bc7565b612493610ca9565b918252868201526124a4828b611856565b526124af818a611856565b506124b990610ba6565b966124c391610bc7565b6124cc91610bc7565b8361242a565b5061242f565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561250357565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b1561254057565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b908151156124ea57602082015180515f1a607f811161259e57505f9250600191839150565b60b78111612611576125af90611b90565b80935111156125ff57600190810151908314906001600160f81b031916816125f1575b506125df57600191905f90565b60405163babb01dd60e01b8152600490fd5b600160ff1b1190505f6125d2565b6040516366c9448560e01b8152600490fd5b60bf81116126745761262290611b81565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df575161265f8483610bc7565b10156125ff5761266e90610bb9565b91905f90565b60f78111612697576126869150611b72565b80925111156125ff57600191908290565b6126a090611b63565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df57516126dd8483610bc7565b10156125ff576126ec90610bb9565b9190600190565b91906126fe816111d3565b9261270c6040519485610c59565b818452612718826111d3565b602090601f19013686830137848315612762575082915f915b83831061274c575050116127425750565b60205f9184010152565b8183015187840182015284935091820191612731565b9450505050565b92919092612776826111d3565b936127846040519586610c59565b828552612790836111d3565b60209190601f190136878401378584156127da57508201809211610bb45782915f915b8383106127c4575050116127425750565b81830151878401820152849350918201916127b3565b955050505050565b6111876040516127f181610c08565b5f81528290815181811c17918260281c9282841860281b1890528151179052565b61118760405161282181610c08565b6807a12000000000000081528290815181811c17918260281c9282841860281b1890528151179052565b906040519061285982610c08565b8060301b8252612881828490815181811c17918260281c9282841860281b1890528151179052565b8060d01c61288e57505090565b6040519081526020810160405260081b60011760281b905290565b9061118790604051906128bb82610c08565b60081b60021760281b81528290815181811c17918260281c9282841860281b1890528151179052565b91906001810192607f8111156129565760029293816fffffffffffffffffffffffffffffffff1060071b82811c6001600160401b031060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff109060031c17916081830184535f5281601f18519052010190565b801560071b179053565b60581b815260158101919060949053565b9190600180820184519160378311156129f6576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260b8840160f81b86528301968280890181878b0101998a9403915b6129d3575b505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f0579190916129bb565b506129c0565b909492918360200151936080855f1a108284141615612a155750505052565b60808294959396979201855360400151846021015252010190565b919060018082018451916037831115612aae576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260f8840160f81b86528301968280890181878b0101998a9403915b612a9157505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f057919091612a7a565b909492918360200151936080855f1a108284141615612acd5750505052565b60c08294959396979201855360400151846021015252010190565b9091815115612be05764ffffffffff60208401925b5116918215612b9a57825180601a1a906001821115612b70575060028114612b5b57600314612b4057612b3a64ffffffffff91845160301c612960565b92612afd565b612b4e90835160301c612ae8565b64ffffffffff9092612afd565b50612b3a64ffffffffff91845160301c612971565b909164ffffffffff9215612b8e57612b3a9150845160301c516128e4565b612b3a9160301c6128e4565b9092908190039150601f1982016037811115612bbd576111879250815280612a30565b5060a082018153602081015160018201526040810151602182015201601e190190565b916001915060c081530190565b60405190612bfa82610be8565b5f6020838281520152565b6040519061042082018281106001600160401b03821117610c03576040526020808352825f5b6104008110612c3957505050565b8290612c43612bed565b82828501015201612c2b56fea264697066735822122039f983ffde1b5c75b55f395dc71d5bea0e96a5a46e7a5b5d1568bca06030e1a164736f6c63430008180033000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4
Deployed Bytecode
0x60806040526004361015610022575b3615610018575f80fd5b6100206112ca565b005b5f3560e01c80630abea268146101815780633d36d9711461017c5780633f4ba83a146101775780635c975abb146101725780636897a52d1461016d578063715018a614610168578063763131a0146101635780638456cb591461015e5780638da5cb5b14610159578063a7ab696114610154578063abc0f4011461014f578063ae1f6aaf1461014a578063b8012cb514610145578063cb23bcb514610140578063cbc43bb91461013b578063d2c13da514610136578063de35f5cb14610131578063e6cd84bf1461012c578063e7a9ad5314610127578063eb12dd5614610122578063ec489d171461011d5763f2fde38b0361000e57610ac8565b610a4a565b6109ed565b610942565b61084f565b610832565b6107cd565b61075d565b610727565b6106fd565b6106d5565b61047c565b61045f565b610438565b6103df565b61035f565b610308565b6102d9565b6102b7565b610224565b6101c5565b346101b05760203660031901126101b0576004355f526007602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160a01b038116036101b057565b346101b05760203660031901126101b0576004356101e2816101b4565b6101ea6113b2565b600354906001600160a01b0390818316610212576001600160a01b0319909216911617600355005b60405163299fdc3960e01b8152600490fd5b346101b0575f3660031901126101b05761023c6113b2565b60025460ff81161561027b5760ff19166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346101b0575f3660031901126101b057602060ff600254166040519015158152f35b346101b05760203660031901126101b0576004355f526004602052602060ff60405f2054166040519015158152f35b346101b0575f3660031901126101b0576103206113b2565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101b05760203660031901126101b05760043561037c816101b4565b6103846113b2565b60028054610100600160a81b03198116600884811b610100600160a81b0316919091179092556001600160a01b0392831692911c167f68cd55ad97baa2df51330d7c27f1e5cffbb01f028c038157ec439e8e4ddfec075f80a3005b346101b0575f3660031901126101b0576103f76113b2565b6103ff611409565b600160ff1960025416176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346101b0575f3660031901126101b0575f546040516001600160a01b039091168152602090f35b346101b0575f3660031901126101b0576020600554604051908152f35b346101b05760603660031901126101b057600480359061049b826101b4565b60243590604435926002600154146106915760026001556104ba611409565b6104c584848361144d565b6104d7815f52600660205260405f2090565b60025460081c6001600160a01b03165f8181526020929092526040909120909390549363ffffffff808660201c16801561068057610527610520865f52600760205260405f2090565b5460ff1690565b61066f5760055461053791610bc7565b42111561065e576040516318feeb1560e31b8152951663ffffffff168286019081526001600160a01b039591610140918291849182908a9082906020010392165afa80156106595761059f92610520925f9261062c575b5050515f52600460205260405f2090565b61061d57507f7effe66fd2d9671e7fc0ed5f9e2ab55fec23fb2f726bc2dc15c02e661528672d93946105ec6105df610611935f52600760205260405f2090565b805460ff19166001179055565b6105f68684611515565b60405193849316958360209093929193604081019481520152565b0390a261002060018055565b604051638edd8c9560e01b8152fd5b61064b9250803d10610652575b6106438183610c59565b810190610cec565b5f8061058e565b503d610639565b610da3565b604051635ca831e160e11b81528390fd5b604051632ba2651560e21b81528490fd5b6040516367ba7fe160e11b81528490fd5b60405162461bcd60e51b8152602081840152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b346101b0575f3660031901126101b0576003546040516001600160a01b039091168152602090f35b346101b05760203660031901126101b0576004355f526008602052602060405f2054604051908152f35b346101b0575f3660031901126101b05760025460405160089190911c6001600160a01b03168152602090f35b801515036101b057565b346101b05760403660031901126101b0576004357f6e5a4355570a915cfb0aafb4210d86823e2f3484b7610255ace1bf5f28534a9260206024356107a081610753565b6107a86113b2565b835f526004825260405f209015159060ff1981541660ff8316179055604051908152a2005b346101b05760203660031901126101b0577f9c3f1b54b1487e018f1d0593ff5cf7fb625b2df6332c974a6cc56bb35887984160043561080a6113b2565b6005548160055561082d6040519283928360209093929193604081019481520152565b0390a1005b346101b0575f3660031901126101b0576020600954604051908152f35b60203660031901126101b057600435610867816101b4565b61086f611409565b6003546001600160a01b039081161561093057341561091e57602091610896600954610dae565b916108a083600955565b6108e36108ab610c9a565b8481526001600160a01b038416868201523460408201526108cb81611559565b6108dd865f52600860205260405f2090565b556115a6565b604051348152911690339083907f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4604051908152f35b60405163fe9ba5cd60e01b8152600490fd5b604051633322339360e11b8152600490fd5b346101b05760603660031901126101b05761095b611409565b600435805f52600860205260405f205461097c61097736610dbc565b611559565b036109db5761099261098d36610dbc565b6115a6565b6024359061099f826101b4565b60405160443581526001600160a01b03909216917f9ed601de5fce88cea72c2f1c91a4868d6144eaa63a5ea75453cbbceb0ef3929190602090a3005b60405163d8ca7f1760e01b8152600490fd5b346101b05760403660031901126101b0576040610a31602435610a0f816101b4565b6004355f526006602052825f209060018060a01b03165f5260205260405f2090565b5463ffffffff825191818116835260201c166020820152f35b346101b0576101203660031901126101b057600435610a68816101b4565b60803660831901126101b05761010435906001600160401b03908183116101b057366023840112156101b05782600401359182116101b0573660248360051b850101116101b0576024610020930190606435906044359060243590610e06565b346101b05760203660031901126101b057600435610ae5816101b4565b610aed6113b2565b6001600160a01b03908116908115610b3e575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b9060018201809211610bb457565b610b92565b6001019081600111610bb457565b91908201809211610bb457565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610c0357604052565b610bd4565b602081019081106001600160401b03821117610c0357604052565b606081019081106001600160401b03821117610c0357604052565b60a081019081106001600160401b03821117610c0357604052565b90601f801991011681019081106001600160401b03821117610c0357604052565b6040519061014082018281106001600160401b03821117610c0357604052565b60405190610ca782610c23565b565b60405190610ca782610be8565b5190610ca7826101b4565b519063ffffffff821682036101b057565b519060058210156101b057565b519060038210156101b057565b90816101409103126101b057610d00610c7a565b9080518252610d1160208201610cb6565b6020830152610d2260408201610cc1565b6040830152610d3360608201610cc1565b6060830152610d4460808201610cc1565b6080830152610d5560a08201610cc1565b60a0830152610d6660c08201610cd2565b60c0830152610d7760e08201610cdf565b60e0830152610100610d8a818301610cb6565b90830152610d9c610120809201610cb6565b9082015290565b6040513d5f823e3d90fd5b5f198114610bb45760010190565b60609060031901126101b05760405190606082018281106001600160401b03821117610c0357604052816004358152602435610df7816101b4565b60208201526040604435910152565b9194929094610e13611409565b610e1e81878561144d565b610e30815f52600660205260405f2090565b60025460081c6001600160a01b0316918291610e72610e6563ffffffff9586939060018060a01b03165f5260205260405f2090565b5460201c63ffffffff1690565b1661116057610e8c610520825f52600760205260405f2090565b61114e5760408051636338049b60e01b8152600480820188905291996001600160a01b039960209392909190868c16908581602481855afa908115610659575f91611121575b5015611111578c516318feeb1560e31b81528481018b81526101409283918391908290819060200103915afa918215610659575f926110f4575b5050610f2461052082515f52600460205260405f2090565b6110e45751610f3a610f353661118a565b61173c565b036110d457610fc191610f94610fbd92610fa08f80519089820182610f6c8d8360205f91939293604081019481520152565b0392610f80601f1994858101835282610c59565b51902090518a810191825294859160200190565b03908101845283610c59565b610fb7610fab6111ee565b9160c435943691611222565b91611777565b1590565b6110c65750926110456110c1959361103061107f946110227fb7f82ff0ef7dc798b8b68a2fb464607fae371501d4b0b87f4123f657dcbe20ce9c9b9a98611006610ca9565b63ffffffff8b89161681529642169087019063ffffffff169052565b5f52600660205260405f2090565b9060018060a01b03165f5260205260405f2090565b9063ffffffff81511667ffffffff0000000060208454938363ffffffff198616178655015160201b16916001600160401b03191617179055565b6002546110a5906110999060081c6001600160a01b031681565b6001600160a01b031690565b9651978852602088015260408701529116939081906060820190565b0390a3565b8951632e1aaf2760e21b8152fd5b8b516315ae1f5d60e31b81528390fd5b8c51638edd8c9560e01b81528490fd5b61110a9250803d10610652576106438183610c59565b5f80610f0c565b8c516307f310d160e41b81528490fd5b6111419150863d8811611147575b6111398183610c59565b810190611172565b5f610ed2565b503d61112f565b604051632ba2651560e21b8152600490fd5b60405163030f885f60e61b8152600490fd5b908160209103126101b0575161118781610753565b90565b60809060831901126101b05760405190608082018281106001600160401b03821117610c035760405281608435815260a435602082015260c4356040820152606060e435910152565b6001600160401b038111610c0357601f01601f191660200190565b604051906111fb82610be8565b60018252600160f81b6020830152565b6001600160401b038111610c035760051b60200190565b9291909261122f8461120b565b916040946112406040519485610c59565b839581855260208095019160051b8401938385116101b05780925b85841061126b5750505050505050565b83356001600160401b0381116101b057820185601f820112156101b057803591611294836111d3565b6112a086519182610c59565b838152878a85850101116101b0575f8a85819682809701838601378301015281520193019261125b565b3332036113855760015b15611373576112e1611409565b6003546001600160a01b03161561093057341561091e57611303600954610dae565b61130c81600955565b611341611317610c9a565b82815233602082015234604082015261132f81611559565b6108dd845f52600860205260405f2090565b604051348152339182917f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4565b60405163770d5e3960e11b8152600490fd5b333b6017036113ac5762ef01006040516020810160405260205f82333c5160e81c146112d4565b5f6112d4565b5f546001600160a01b031633036113c557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60ff6002541661141557565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b604091825191602083019160018060a01b0380921683528484015283835261147483610c23565b600354169280519260c08401938085106001600160401b03861117610c035760a0829161150f96855288815287602082015230858201525f60608201525f608082015201528151948592602084019788528301523060608301525f60808301525f60a083015260c080830152518060e08301526114f88161010094858501906117b7565b601f801991011681010360e0810184520182610c59565b51902090565b81471061154c575f38818085856108fcf11561152f575050565b601691600b915f526073825360ff602053f01561154857565b3838fd5b63b12d13eb5f526004601cfd5b805190604060018060a01b0360208301511691015160405191602083019384526040830152606082015260608152608081018181106001600160401b03821117610c035760405251902090565b604080519181602091636093e77160e01b838601528051602486015260018060a01b038382015116604486015201516064840152606483526115e783610c3e565b600354928251936bffffffffffffffffffffffff199060601b16828501526014845261161284610be8565b82519061161e82610c08565b5f8252466001036116d757620face79493926116846116d29361167d6021946116628a995b61165b8a519b6116528d610c08565b5f8d528c61284b565b508a6128a9565b5061166c896127e2565b5061167689612812565b50886128a9565b50866128a9565b508351946116c26116988388018093612ae8565b92601f198885030188525f8452830195868152830196602360f91b885251809260418501906117b7565b8101036001810184520182610c59565b5190a1565b4662aa36a70361170257620face79493926116846116d29361167d60219461166262face7a99611643565b835162461bcd60e51b8152600481018490526013602482015272155b9cdd5c1c1bdc9d19590818da185a5b9259606a1b6044820152606490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261150f81610c3e565b926117a3919261118794602081519101206040519060208201526020815261179e81610be8565b611d02565b906020815191012090602081519101201490565b5f5b8381106117c85750505f910152565b81810151838201526020016117b9565b156117df57565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b5f52603260045260245ffd5b8051600110156118405760400190565b61181c565b805160101015611840576102200190565b80518210156118405760209160051b010190565b1561187157565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b156118d457565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b1561192f57565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b1561198b57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b8051156118405760200190565b908151811015611840570160200190565b60ff166002039060ff8211610bb457565b15611a0657565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15611a7857565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15611aea57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b5f19810191908211610bb457565b60f619810191908211610bb457565b60bf19810191908211610bb457565b60b619810191908211610bb457565b607f19810191908211610bb457565b91908203918211610bb457565b15611bb357565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15611c2557565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15611c9757565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190611d1c611d2291611d17855115156117d8565b6120d9565b92612192565b90604080519081611d3b60209485830160209181520190565b0391611d4f601f1993848101835282610c59565b935f925f945b875186101561208757611d688689611856565b5196611d77845187111561186a565b8561203357611dc1611dc691611da2611dae8b51868151910120895192839188830160209181520190565b03878101835282610c59565b6020815191012090602081519101201490565b611984565b80870196875151601181145f14611e7257505082518503611e18575050505050611e12611dff611df96111879451611845565b51612365565b93611e0c85511515611c1e565b51611b55565b14611c90565b909192949395611e66611e60611e5a60019360ff611e50611e4a611e3c8e8c6119dd565b516001600160f81b03191690565b60f81c90565b9151911690611856565b51612323565b97610ba6565b945b0194929190611d55565b94929099989795939160028096145f14611fde57611e9360ff999a9b6121e6565b95611ea3611e4a611e3c896119d0565b96611ec4600191611ebe611eb8848c166119ee565b60ff1690565b90612206565b9a8b98611ee7611ede611ed78b8a612206565b809c6122bd565b809e51146119ff565b169182148015611fd4575b15611f305750505050505050611df9611f2a92611f1761118796611f1d945114611a71565b51611830565b93611e0c85511515611ae3565b14611bac565b90809295999893969a94975015918215611fca575b505015611f6b57600191611f5f611e5a611f659351611830565b98610bc7565b94611e68565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b1490505f80611f45565b5060038214611ef2565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b87518051831161206e575061206461206991611da2611dae8b51868151910120895192839188830160209181520190565b611928565b611dc6565b80516020918201208251919092012061206991146118cd565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b9081516120e58161120b565b60406120f46040519283610c59565b828252601f196121038461120b565b01905f5b82811061216f5750505080935f5b8381106121225750505050565b8061212f60019284611856565b5161214361213d8386611856565b516123c1565b61214b610ca9565b918252602082015261215d8286611856565b526121688185611856565b5001612115565b602090825161217d81610be8565b60608082528382015285820183015201612107565b6040519080516001918160011b603f8101601f1916850160405284525f5b8281106121be575050505090565b808491821b86016021600f60208085880101515f1a908160041c9085015316910153016121b0565b60200151805115611840576122016020611187920151612365565b612192565b9081518110156122a957815181810390808211610bb457829061222e83601f810110156124fc565b612249838301612240848210156124fc565b86511015612539565b03612261575050506040515f81526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106122965750508252601f01601f191660405290565b909283518152602080910193019061227e565b50506040516122b781610c08565b5f815290565b805182515f94939185918082101561231a575094505b5b848110806122f0575b156122ea576001016122d4565b93505050565b506001600160f81b03198061230583866119dd565b51169061231283856119dd565b5116146122dd565b905094506122d3565b60208151105f1461233e5760208101519051611187916126f3565b61118790612365565b6002111561235157565b634e487b7160e01b5f52602160045260245ffd5b61236e81612579565b6002819392931015612351576123af57825182820190818311610bb4570361239d576020611187930151612769565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6123c9612bed565b508051156124ea576020815191604051926123e384610be8565b83526020808401910181526123f783612579565b91939061240383612347565b60018093036124d8576124169085610bc7565b85510361239d57612425612c05565b945f94835b612439575b5050505050815290565b8151808210156124d2578161244d91611b9f565b8186519061245a91610bc7565b612462610ca9565b9182528482015261247290612579565b509661247e8289610bc7565b8388519061248b91610bc7565b612493610ca9565b918252868201526124a4828b611856565b526124af818a611856565b506124b990610ba6565b966124c391610bc7565b6124cc91610bc7565b8361242a565b5061242f565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561250357565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b1561254057565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b908151156124ea57602082015180515f1a607f811161259e57505f9250600191839150565b60b78111612611576125af90611b90565b80935111156125ff57600190810151908314906001600160f81b031916816125f1575b506125df57600191905f90565b60405163babb01dd60e01b8152600490fd5b600160ff1b1190505f6125d2565b6040516366c9448560e01b8152600490fd5b60bf81116126745761262290611b81565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df575161265f8483610bc7565b10156125ff5761266e90610bb9565b91905f90565b60f78111612697576126869150611b72565b80925111156125ff57600191908290565b6126a090611b63565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df57516126dd8483610bc7565b10156125ff576126ec90610bb9565b9190600190565b91906126fe816111d3565b9261270c6040519485610c59565b818452612718826111d3565b602090601f19013686830137848315612762575082915f915b83831061274c575050116127425750565b60205f9184010152565b8183015187840182015284935091820191612731565b9450505050565b92919092612776826111d3565b936127846040519586610c59565b828552612790836111d3565b60209190601f190136878401378584156127da57508201809211610bb45782915f915b8383106127c4575050116127425750565b81830151878401820152849350918201916127b3565b955050505050565b6111876040516127f181610c08565b5f81528290815181811c17918260281c9282841860281b1890528151179052565b61118760405161282181610c08565b6807a12000000000000081528290815181811c17918260281c9282841860281b1890528151179052565b906040519061285982610c08565b8060301b8252612881828490815181811c17918260281c9282841860281b1890528151179052565b8060d01c61288e57505090565b6040519081526020810160405260081b60011760281b905290565b9061118790604051906128bb82610c08565b60081b60021760281b81528290815181811c17918260281c9282841860281b1890528151179052565b91906001810192607f8111156129565760029293816fffffffffffffffffffffffffffffffff1060071b82811c6001600160401b031060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff109060031c17916081830184535f5281601f18519052010190565b801560071b179053565b60581b815260158101919060949053565b9190600180820184519160378311156129f6576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260b8840160f81b86528301968280890181878b0101998a9403915b6129d3575b505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f0579190916129bb565b506129c0565b909492918360200151936080855f1a108284141615612a155750505052565b60808294959396979201855360400151846021015252010190565b919060018082018451916037831115612aae576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260f8840160f81b86528301968280890181878b0101998a9403915b612a9157505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f057919091612a7a565b909492918360200151936080855f1a108284141615612acd5750505052565b60c08294959396979201855360400151846021015252010190565b9091815115612be05764ffffffffff60208401925b5116918215612b9a57825180601a1a906001821115612b70575060028114612b5b57600314612b4057612b3a64ffffffffff91845160301c612960565b92612afd565b612b4e90835160301c612ae8565b64ffffffffff9092612afd565b50612b3a64ffffffffff91845160301c612971565b909164ffffffffff9215612b8e57612b3a9150845160301c516128e4565b612b3a9160301c6128e4565b9092908190039150601f1982016037811115612bbd576111879250815280612a30565b5060a082018153602081015160018201526040810151602182015201601e190190565b916001915060c081530190565b60405190612bfa82610be8565b5f6020838281520152565b6040519061042082018281106001600160401b03821117610c03576040526020808352825f5b6104008110612c3957505050565b8290612c43612bed565b82828501015201612c2b56fea264697066735822122039f983ffde1b5c75b55f395dc71d5bea0e96a5a46e7a5b5d1568bca06030e1a164736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4
-----Decoded View---------------
Arg [0] : _rollup (address): 0x686E7d01C7BFCB563721333A007699F154C04eb4
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $3,204.69 | 0.054 | $173.05 |
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.