Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LightLinkPortal
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeCall} from "../libraries/SafeCall.sol";
// import {L2OutputOracle} from "../L1/L2OutputOracle.sol";
import {ICanonicalStateChain} from "./interfaces/ICanonicalStateChain.sol";
import {IChallengeBase} from "./interfaces/IChallengeBase.sol";
// import {SuperchainConfig} from "src/L1/SuperchainConfig.sol";
import {Constants} from "../libraries/Constants.sol";
import {Types} from "../libraries/Types.sol";
import {Hashing} from "../libraries/Hashing.sol";
import {SecureMerkleTrie} from "../libraries/SecureMerkleTrie.sol";
import {AddressAliasHelper} from "../libraries/AddressAliasHelper.sol";
import {ResourceMetering} from "../L1/ResourceMetering.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {L1Block} from "../L2/L1Block.sol";
import {Predeploys} from "../libraries/Predeploys.sol";
import "../libraries/PortalErrors.sol";
import "../libraries/Pausible.sol";
/// @custom:proxied
/// @title LightLinkPortal
/// @notice The LightLinkPortal is a low-level contract responsible for passing messages between L1
/// and L2. Messages sent directly to the LightLinkPortal have no form of replayability.
/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface.
contract LightLinkPortal is Initializable, ResourceMetering, Ownable, Pausable {
/// @notice Allows for interactions with non standard ERC20 tokens.
using SafeERC20 for IERC20;
/// @notice Represents a proven withdrawal.
/// @custom:field outputRoot Root of the L2 output this was proven against.
/// @custom:field timestamp Timestamp at whcih the withdrawal was proven.
/// @custom:field l2OutputIndex Index of the output this was proven against.
struct ProvenWithdrawal {
bytes32 outputRoot;
uint128 timestamp;
uint128 l2OutputIndex;
}
/// @notice Version of the deposit event.
uint256 internal constant DEPOSIT_VERSION = 0;
/// @notice The L2 gas limit set when eth is deposited using the receive() function.
uint64 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000;
/// @notice The L2 gas limit for system deposit transactions that are initiated from L1.
uint32 internal constant SYSTEM_DEPOSIT_GAS_LIMIT = 200_000;
/// @notice Address of the L2 account which initiated a withdrawal in this transaction.
/// If the of this variable is the default L2 sender address, then we are NOT inside of
/// a call to finalizeWithdrawalTransaction.
address public l2Sender;
/// @notice A list of withdrawal hashes which have been successfully finalized.
mapping(bytes32 => bool) public finalizedWithdrawals;
/// @notice A mapping of withdrawal hashes to `ProvenWithdrawal` data.
mapping(bytes32 => ProvenWithdrawal) public provenWithdrawals;
ResourceConfig public resourceConfig;
/// @notice Contract of the L2OutputOracle.
/// @custom:network-specific
ICanonicalStateChain public l2Oracle;
/// @notice Contract of the ChallengeBase.
IChallengeBase challenge;
/// @notice Represents the amount of native asset minted in L2. This may not
/// be 100% accurate due to the ability to send ether to the contract
/// without triggering a deposit transaction. It also is used to prevent
/// overflows for L2 account balances when custom gas tokens are used.
/// It is not safe to trust `ERC20.balanceOf` as it may lie.
uint256 internal _balance;
/// @notice Emitted when a transaction is deposited from L1 to L2.
/// The parameters of this event are read by the rollup node and used to derive deposit
/// transactions on L2.
/// @param from Address that triggered the deposit transaction.
/// @param to Address that the deposit transaction is directed to.
/// @param version Version of this deposit transaction event.
/// @param opaqueData ABI encoded deposit data to be parsed off-chain.
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 indexed version,
bytes opaqueData
);
/// @notice Emitted when a withdrawal transaction is proven.
/// @param withdrawalHash Hash of the withdrawal transaction.
/// @param from Address that triggered the withdrawal transaction.
/// @param to Address that the withdrawal transaction is directed to.
event WithdrawalProven(
bytes32 indexed withdrawalHash,
address indexed from,
address indexed to
);
/// @notice Emitted when a withdrawal transaction is finalized.
/// @param withdrawalHash Hash of the withdrawal transaction.
/// @param success Whether the withdrawal transaction was successful.
event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success);
/// @notice Semantic version.
/// @custom:semver 2.8.1-beta.1
function version() public pure virtual returns (string memory) {
return "2.8.1-beta.1";
}
/// @notice Constructs the LightLinkPortal contract.
constructor() Pausable(msg.sender) {
initialize({
_l2Oracle: ICanonicalStateChain(address(0)),
_challenge: IChallengeBase(address(0)),
_newOwner: address(0)
});
}
/// @notice Initializer.
/// @param _l2Oracle Contract of the L2OutputOracle.
/// @param _challenge Contract of the ChallengeBase.
function initialize(
ICanonicalStateChain _l2Oracle,
IChallengeBase _challenge,
address _newOwner
) public initializer {
l2Oracle = _l2Oracle;
challenge = _challenge;
resourceConfig = ResourceConfig({
maxResourceLimit: 20_000_000,
elasticityMultiplier: 10,
baseFeeMaxChangeDenominator: 8,
minimumBaseFee: 1 gwei,
systemTxMaxGas: 1_000_000,
maximumBaseFee: type(uint128).max
});
if (l2Sender == address(0)) {
l2Sender = Constants.DEFAULT_L2_SENDER;
}
_transferOwnership(_newOwner);
__ResourceMetering_init();
}
/// @notice Getter for the balance of the contract.
function balance() public view returns (uint256) {
(address token, ) = gasPayingToken();
if (token == Constants.ETHER) {
return address(this).balance;
} else {
return _balance;
}
}
/// @notice Computes the minimum gas limit for a deposit.
/// The minimum gas limit linearly increases based on the size of the calldata.
/// This is to prevent users from creating L2 resource usage without paying for it.
/// This function can be used when interacting with the portal to ensure forwards
/// compatibility.
/// @param _byteCount Number of bytes in the calldata.
/// @return The minimum gas limit for a deposit.
function minimumGasLimit(uint64 _byteCount) public pure returns (uint64) {
return _byteCount * 16 + 21000;
}
/// @notice Accepts value so that users can send ETH directly to this contract and have the
/// funds be deposited to their address on L2. This is intended as a convenience
/// function for EOAs. Contracts should call the depositTransaction() function directly
/// otherwise any deposited funds will be lost due to address aliasing.
receive() external payable {
depositTransaction(
msg.sender,
msg.value,
RECEIVE_DEFAULT_GAS_LIMIT,
false,
bytes("")
);
}
/// @notice Accepts ETH value without triggering a deposit to L2.
/// This function mainly exists for the sake of the migration between the legacy
/// Optimism system and Bedrock.
function donateETH() external payable {
// Intentionally empty.
}
/// @notice Returns the gas paying token and its decimals.
function gasPayingToken()
internal
view
returns (address addr_, uint8 decimals_)
{
// (addr_, decimals_) = systemConfig.gasPayingToken();
// TODO: Uncomment the above line when the gas paying token is implemented.
addr_ = Constants.ETHER;
decimals_ = 18;
}
/// @notice Getter for the resource config.
/// Used internally by the ResourceMetering contract.
/// The SystemConfig is the source of truth for the resource config.
/// @return ResourceMetering ResourceConfig
function _resourceConfig()
internal
view
override
returns (ResourceMetering.ResourceConfig memory)
{
return resourceConfig;
}
/// @notice Proves a withdrawal transaction.
/// @param _tx Withdrawal transaction to finalize.
/// @param _l2OutputIndex L2 output index to prove against.
/// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root.
/// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract.
function proveWithdrawalTransaction(
Types.WithdrawalTransaction memory _tx,
uint256 _l2OutputIndex,
Types.OutputRootProof calldata _outputRootProof,
bytes[] calldata _withdrawalProof
) external whenNotPaused {
// Prevent users from creating a deposit transaction where this address is the message
// sender on L2. Because this is checked here, we do not need to check again in
// `finalizeWithdrawalTransaction`.
if (_tx.target == address(this)) revert BadTarget();
// Get the output root and load onto the stack to prevent multiple mloads. This will
// revert if there is no output root for the given block number.
bytes32 outputRoot = l2Oracle.getL2Output(_l2OutputIndex).outputRoot;
// Verify that the output root can be generated with the elements in the proof.
require(
outputRoot == Hashing.hashOutputRootProof(_outputRootProof),
"LightLinkPortal: invalid output root proof"
);
// Load the ProvenWithdrawal into memory, using the withdrawal hash as a unique identifier.
bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx);
ProvenWithdrawal memory provenWithdrawal = provenWithdrawals[
withdrawalHash
];
// We generally want to prevent users from proving the same withdrawal multiple times
// because each successive proof will update the timestamp. A malicious user can take
// advantage of this to prevent other users from finalizing their withdrawal. However,
// since withdrawals are proven before an output root is finalized, we need to allow users
// to re-prove their withdrawal only in the case that the output root for their specified
// output index has been updated.
require(
provenWithdrawal.timestamp == 0 ||
l2Oracle
.getL2Output(provenWithdrawal.l2OutputIndex)
.outputRoot !=
provenWithdrawal.outputRoot,
"LightLinkPortal: withdrawal hash has already been proven"
);
// Compute the storage slot of the withdrawal hash in the L2ToL1MessagePasser contract.
// Refer to the Solidity documentation for more information on how storage layouts are
// computed for mappings.
bytes32 storageKey = keccak256(
abi.encode(
withdrawalHash,
uint256(0) // The withdrawals mapping is at the first slot in the layout.
)
);
// Verify that the hash of this withdrawal was stored in the L2toL1MessagePasser contract
// on L2. If this is true, under the assumption that the SecureMerkleTrie does not have
// bugs, then we know that this withdrawal was actually triggered on L2 and can therefore
// be relayed on L1.
require(
SecureMerkleTrie.verifyInclusionProof({
_key: abi.encode(storageKey),
_value: hex"01",
_proof: _withdrawalProof,
_root: _outputRootProof.messagePasserStorageRoot
}),
"LightLinkPortal: invalid withdrawal inclusion proof"
);
// Designate the withdrawalHash as proven by storing the `outputRoot`, `timestamp`, and
// `l2BlockNumber` in the `provenWithdrawals` mapping. A `withdrawalHash` can only be
// proven once unless it is submitted again with a different outputRoot.
provenWithdrawals[withdrawalHash] = ProvenWithdrawal({
outputRoot: outputRoot,
timestamp: uint128(block.timestamp),
l2OutputIndex: uint128(_l2OutputIndex)
});
// Emit a `WithdrawalProven` event.
emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target);
}
/// @notice Finalizes a withdrawal transaction.
/// @param _tx Withdrawal transaction to finalize.
function finalizeWithdrawalTransaction(
Types.WithdrawalTransaction memory _tx
) external whenNotPaused {
// Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other
// than the default value when a withdrawal transaction is being finalized. This check is
// a defacto reentrancy guard.
if (l2Sender != Constants.DEFAULT_L2_SENDER) revert NonReentrant();
// Grab the proven withdrawal from the `provenWithdrawals` map.
bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx);
ProvenWithdrawal memory provenWithdrawal = provenWithdrawals[
withdrawalHash
];
// A withdrawal can only be finalized if it has been proven. We know that a withdrawal has
// been proven at least once when its timestamp is non-zero. Unproven withdrawals will have
// a timestamp of zero.
require(
provenWithdrawal.timestamp != 0,
"LightLinkPortal: withdrawal has not been proven yet"
);
// As a sanity check, we make sure that the proven withdrawal's timestamp is greater than
// starting timestamp inside the L2OutputOracle. Not strictly necessary but extra layer of
// safety against weird bugs in the proving step.
require(
provenWithdrawal.timestamp >= l2Oracle.startingTimestamp(),
"LightLinkPortal: withdrawal timestamp less than L2 Oracle starting timestamp"
);
// A proven withdrawal must wait at least the finalization period before it can be
// finalized. This waiting period can elapse in parallel with the waiting period for the
// output the withdrawal was proven against. In effect, this means that the minimum
// withdrawal time is proposal submission time + finalization period.
require(
_isFinalizationPeriodElapsed(provenWithdrawal.timestamp),
"LightLinkPortal: proven withdrawal finalization period has not elapsed"
);
// Grab the OutputProposal from the L2OutputOracle, will revert if the output that
// corresponds to the given index has not been proposed yet.
ICanonicalStateChain.Output memory proposal = l2Oracle.getL2Output(
provenWithdrawal.l2OutputIndex
);
// Check that the output root that was used to prove the withdrawal is the same as the
// current output root for the given output index. An output root may change if it is
// deleted by the challenger address and then re-proposed.
require(
proposal.outputRoot == provenWithdrawal.outputRoot,
"LightLinkPortal: output root proven is not the same as current output root"
);
// Check that the output proposal has also been finalized.
require(
_isFinalizationPeriodElapsed(proposal.timestamp),
"LightLinkPortal: output proposal finalization period has not elapsed"
);
// Check that this withdrawal has not already been finalized, this is replay protection.
require(
finalizedWithdrawals[withdrawalHash] == false,
"LightLinkPortal: withdrawal has already been finalized"
);
// Mark the withdrawal as finalized so it can't be replayed.
finalizedWithdrawals[withdrawalHash] = true;
// Set the l2Sender so contracts know who triggered this withdrawal on L2.
// This acts as a reentrancy guard.
l2Sender = _tx.sender;
bool success;
(address token, ) = gasPayingToken();
if (token == Constants.ETHER) {
// Trigger the call to the target contract. We use a custom low level method
// SafeCall.callWithMinGas to ensure two key properties
// 1. Target contracts cannot force this call to run out of gas by returning a very large
// amount of data (and this is OK because we don't care about the returndata here).
// 2. The amount of gas provided to the execution context of the target is at least the
// gas limit specified by the user. If there is not enough gas in the current context
// to accomplish this, `callWithMinGas` will revert.
success = SafeCall.callWithMinGas(
_tx.target,
_tx.gasLimit,
_tx.value,
_tx.data
);
} else {
// Cannot call the token contract directly from the portal. This would allow an attacker
// to call approve from a withdrawal and drain the balance of the portal.
if (_tx.target == token) revert BadTarget();
// Only transfer value when a non zero value is specified. This saves gas in the case of
// using the standard bridge or arbitrary message passing.
if (_tx.value != 0) {
// Update the contracts internal accounting of the amount of native asset in L2.
_balance -= _tx.value;
// Read the balance of the target contract before the transfer so the consistency
// of the transfer can be checked afterwards.
uint256 startBalance = IERC20(token).balanceOf(address(this));
// Transfer the ERC20 balance to the target, accounting for non standard ERC20
// implementations that may not return a boolean. This reverts if the low level
// call is not successful.
IERC20(token).safeTransfer({to: _tx.target, value: _tx.value});
// The balance must be transferred exactly.
if (
IERC20(token).balanceOf(address(this)) !=
startBalance - _tx.value
) {
revert TransferFailed();
}
}
// Make a call to the target contract only if there is calldata.
if (_tx.data.length != 0) {
success = SafeCall.callWithMinGas(
_tx.target,
_tx.gasLimit,
0,
_tx.data
);
} else {
success = true;
}
}
// Reset the l2Sender back to the default value.
l2Sender = Constants.DEFAULT_L2_SENDER;
// All withdrawals are immediately finalized. Replayability can
// be achieved through contracts built on top of this contract
emit WithdrawalFinalized(withdrawalHash, success);
// Reverting here is useful for determining the exact gas cost to successfully execute the
// sub call to the target contract if the minimum gas limit specified by the user would not
// be sufficient to execute the sub call.
if (success == false && tx.origin == Constants.ESTIMATION_ADDRESS) {
revert GasEstimation();
}
}
/// @notice Entrypoint to depositing an ERC20 token as a custom gas token.
/// This function depends on a well formed ERC20 token. There are only
/// so many checks that can be done on chain for this so it is assumed
/// that chain operators will deploy chains with well formed ERC20 tokens.
/// @param _to Target address on L2.
/// @param _mint Units of ERC20 token to deposit into L2.
/// @param _value Units of ERC20 token to send on L2 to the recipient.
/// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1.
/// @param _isCreation Whether or not the transaction is a contract creation.
/// @param _data Data to trigger the recipient with.
function depositERC20Transaction(
address _to,
uint256 _mint,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) public metered(_gasLimit) {
// Can only be called if an ERC20 token is used for gas paying on L2
(address token, ) = gasPayingToken();
if (token == Constants.ETHER) revert OnlyCustomGasToken();
// Gives overflow protection for L2 account balances.
_balance += _mint;
// Get the balance of the portal before the transfer.
uint256 startBalance = IERC20(token).balanceOf(address(this));
// Take ownership of the token. It is assumed that the user has given the portal an approval.
IERC20(token).safeTransferFrom({
from: msg.sender,
to: address(this),
value: _mint
});
// Double check that the portal now has the exact amount of token.
if (IERC20(token).balanceOf(address(this)) != startBalance + _mint) {
revert TransferFailed();
}
_depositTransaction({
_to: _to,
_mint: _mint,
_value: _value,
_gasLimit: _gasLimit,
_isCreation: _isCreation,
_data: _data
});
}
/// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
/// deriving deposit transactions. Note that if a deposit is made by a contract, its
/// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider
/// using the CrossDomainMessenger contracts for a simpler developer experience.
/// @param _to Target address on L2.
/// @param _value ETH value to send to the recipient.
/// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1.
/// @param _isCreation Whether or not the transaction is a contract creation.
/// @param _data Data to trigger the recipient with.
function depositTransaction(
address _to,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) public payable metered(_gasLimit) {
(address token, ) = gasPayingToken();
if (token != Constants.ETHER && msg.value != 0) revert NoValue();
_depositTransaction({
_to: _to,
_mint: msg.value,
_value: _value,
_gasLimit: _gasLimit,
_isCreation: _isCreation,
_data: _data
});
}
/// @notice Common logic for creating deposit transactions.
/// @param _to Target address on L2.
/// @param _mint Units of asset to deposit into L2.
/// @param _value Units of asset to send on L2 to the recipient.
/// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1.
/// @param _isCreation Whether or not the transaction is a contract creation.
/// @param _data Data to trigger the recipient with.
function _depositTransaction(
address _to,
uint256 _mint,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) internal {
// Just to be safe, make sure that people specify address(0) as the target when doing
// contract creations.
if (_isCreation && _to != address(0)) revert BadTarget();
// Prevent depositing transactions that have too small of a gas limit. Users should pay
// more for more resource usage.
if (_gasLimit < minimumGasLimit(uint64(_data.length)))
revert SmallGasLimit();
// Prevent the creation of deposit transactions that have too much calldata. This gives an
// upper limit on the size of unsafe blocks over the p2p network. 120kb is chosen to ensure
// that the transaction can fit into the p2p network policy of 128kb even though deposit
// transactions are not gossipped over the p2p network.
if (_data.length > 120_000) revert LargeCalldata();
// Transform the from-address to its alias if the caller is a contract.
address from = msg.sender;
if (msg.sender != tx.origin) {
from = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
}
// Compute the opaque data that will be emitted as part of the TransactionDeposited event.
// We use opaque data so that we can update the TransactionDeposited event in the future
// without breaking the current interface.
bytes memory opaqueData = abi.encodePacked(
_mint,
_value,
_gasLimit,
_isCreation,
_data
);
// Emit a TransactionDeposited event so that the rollup node can derive a deposit
// transaction for this deposit.
emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData);
}
/// @notice Sets the gas paying token for the L2 system. This token is used as the
/// L2 native asset. Only the SystemConfig contract can call this function.
function setGasPayingToken(
address _token,
uint8 _decimals,
bytes32 _name,
bytes32 _symbol
) external onlyOwner {
// Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas.
// This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`.
useGas(SYSTEM_DEPOSIT_GAS_LIMIT);
// Emit the special deposit transaction directly that sets the gas paying
// token in the L1Block predeploy contract.
emit TransactionDeposited(
Constants.DEPOSITOR_ACCOUNT,
Predeploys.L1_BLOCK_ATTRIBUTES,
DEPOSIT_VERSION,
abi.encodePacked(
uint256(0), // mint
uint256(0), // value
uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit
false, // isCreation,
abi.encodeCall(
L1Block.setGasPayingToken,
(_token, _decimals, _name, _symbol)
)
)
);
}
/// @notice Determine if a given output is finalized.
/// Reverts if the call to l2Oracle.getL2Output reverts.
/// Returns a boolean otherwise.
/// @param _l2OutputIndex Index of the L2 output to check.
/// @return Whether or not the output is finalized.
function isOutputFinalized(
uint256 _l2OutputIndex
) external view returns (bool) {
return
_isFinalizationPeriodElapsed(
l2Oracle.getL2Output(_l2OutputIndex).timestamp
);
}
/// @notice Determines whether the finalization period has elapsed with respect to
/// the provided block timestamp.
/// @param _timestamp Timestamp to check.
/// @return Whether or not the finalization period has elapsed.
function _isFinalizationPeriodElapsed(
uint256 _timestamp
) internal view returns (bool) {
return block.timestamp > _timestamp + challenge.finalizationSeconds();
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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 (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ICanonicalStateChain {
/// @notice The header struct represents a block header in the rollup chain.
/// @param epoch - Epoch refers to a block number on the Ethereum blockchain.
/// @param l2Height - L2Height is the index of the Last L2 Block in this bundle.
/// @param prevHash - PrevHash is the hash of the previous block bundle.
/// @param outputRoot - The output root = keccack(version_hash || keccack(state_root || withdrawal_root || latest_block_hash))
/// @param celestiaPointers - Pointer to the blocks contents on celestia.
/// See `Span` from https://docs.celestia.org/developers/blobstream-offchain#defining-a-chain
struct Header {
uint64 epoch;
uint64 l2Height;
bytes32 prevHash;
bytes32 outputRoot;
CelestiaPointer[] celestiaPointers;
}
/// @notice A pointer to a shares on Celestia.
/// @param height - The height of the block on Celestia.
/// @param shareStart - The start index of shares in block on Celestia.
/// @param shareLen - The length of the shares in block on Celestia.
struct CelestiaPointer {
uint64 height;
uint24 shareStart;
uint16 shareLen;
}
/// @notice The metadata of a block header.
/// @param timestamp - The timestamp the block was added.
/// @param publisher - The address of the publisher that added the block.
struct HeaderMetadata {
uint64 timestamp;
address publisher;
}
/// @notice Emitted when a new block is added to the chain.
/// @param blockNumber - The block number of the new block.
event BlockAdded(uint256 indexed blockNumber);
/// @notice Emitted when the chain is rolled back.
/// @param blockNumber - The block number the chain was rolled back to.
event Rolledback(uint256 indexed blockNumber);
/// @notice Emitted when the publisher address is changed.
/// @param publisher - The new publisher address.
event PublisherChanged(address indexed publisher);
/// @notice The address of the publisher. Publisher is the verified address
/// that can add new blocks to the chain. This address can be
/// replaced by the owner of the contract, (expected to be the
/// rollup contract).
/// @return The address of the publisher.
function publisher() external view returns (address);
/// @notice The address of the challenge contract. Challenge is the address
/// of the challenge contract. This contract can rollback the chain
/// after a successful challenge is made.
/// @return The address of the challenge contract.
function challenge() external view returns (address);
/// @notice The index of the last block in the chain.
/// @return The index of the last block in the chain.
function chainHead() external view returns (uint256);
/// @notice The canonical chain of block headers.
/// @return The block header.
function headers(bytes32) external view returns (Header memory);
/// @notice Returns the block header by hash.
/// @return The block header.
function getHeaderByHash(bytes32) external view returns (Header memory);
/// @notice The metadata of a block header.
/// @return The metadata of a block header.
function headerMetadata(
bytes32
) external view returns (HeaderMetadata memory);
/// @notice Returns the block hash by number.
/// @return The block hash.
function chain(uint256) external view returns (bytes32);
/// @notice Optimistically pushes block headers to the canonical chain.
/// The only fields that are checked are the epoch and prevHash.
/// @param _header - The block header to push.
function pushBlock(Header calldata _header) external;
/// @notice Returns the hash of a block header.
/// @param _header - The block header to hash.
/// @return The hash of the block header.
function hash(Header memory _header) external pure returns (bytes32);
/// @notice Returns the hash of a block header.
/// @param _index - The block number of the header.
/// @return The hash of the block header.
function getHeaderByNum(
uint256 _index
) external view returns (Header memory);
/// @notice Returns the header of the last block in the chain.
/// @return The header of the last block in the chain.
function getHead() external view returns (Header memory);
struct Output {
bytes32 outputRoot;
uint64 timestamp;
}
/// @notice get the output of a block.
/// @param _index - The block number of the output.
/// @return The output of the block.
function getL2Output(uint256 _index) external view returns (Output memory);
/// @notice Returns the starting timestamp of the chain.
/// @return The starting timestamp of the chain.
function startingTimestamp() external view returns (uint64);
/// @notice Rolls back the chain to a previous block number. Reverts
/// the chain to a previous state, It can only be called by
/// the challenge contract.
/// @param _blockNumber - The block number to rollback to.
/// @param _blockhash - The block hash to rollback to.
function rollback(uint256 _blockNumber, bytes32 _blockhash) external;
/// @notice Sets the publisher address.
/// @param _publisher - The new publisher address.
function setPublisher(address _publisher) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
interface IChallengeBase {
/// @return The total time in seconds for a block to be finalized.
function finalizationSeconds() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Burn} from "../libraries/Burn.sol";
import {Arithmetic} from "../libraries/Arithmetic.sol";
/// @custom:upgradeable
/// @title ResourceMetering
/// @notice ResourceMetering implements an EIP-1559 style resource metering system where pricing
/// updates automatically based on current demand.
abstract contract ResourceMetering is Initializable {
/// @notice Error returned when too much gas resource is consumed.
error OutOfGas();
/// @notice Represents the various parameters that control the way in which resources are
/// metered. Corresponds to the EIP-1559 resource metering system.
/// @custom:field prevBaseFee Base fee from the previous block(s).
/// @custom:field prevBoughtGas Amount of gas bought so far in the current block.
/// @custom:field prevBlockNum Last block number that the base fee was updated.
struct ResourceParams {
uint128 prevBaseFee;
uint64 prevBoughtGas;
uint64 prevBlockNum;
}
/// @notice Represents the configuration for the EIP-1559 based curve for the deposit gas
/// market. These values should be set with care as it is possible to set them in
/// a way that breaks the deposit gas market. The target resource limit is defined as
/// maxResourceLimit / elasticityMultiplier. This struct was designed to fit within a
/// single word. There is additional space for additions in the future.
/// @custom:field maxResourceLimit Represents the maximum amount of deposit gas that
/// can be purchased per block.
/// @custom:field elasticityMultiplier Determines the target resource limit along with
/// the resource limit.
/// @custom:field baseFeeMaxChangeDenominator Determines max change on fee per block.
/// @custom:field minimumBaseFee The min deposit base fee, it is clamped to this
/// value.
/// @custom:field systemTxMaxGas The amount of gas supplied to the system
/// transaction. This should be set to the same
/// number that the op-node sets as the gas limit
/// for the system transaction.
/// @custom:field maximumBaseFee The max deposit base fee, it is clamped to this
/// value.
struct ResourceConfig {
uint32 maxResourceLimit;
uint8 elasticityMultiplier;
uint8 baseFeeMaxChangeDenominator;
uint32 minimumBaseFee;
uint32 systemTxMaxGas;
uint128 maximumBaseFee;
}
/// @notice EIP-1559 style gas parameters.
ResourceParams public params;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
uint256[48] private __gap;
/// @notice Meters access to a function based an amount of a requested resource.
/// @param _amount Amount of the resource requested.
modifier metered(uint64 _amount) {
// Record initial gas amount so we can refund for it later.
uint256 initialGas = gasleft();
// Run the underlying function.
_;
// Run the metering function.
_metered(_amount, initialGas);
}
/// @notice An internal function that holds all of the logic for metering a resource.
/// @param _amount Amount of the resource requested.
/// @param _initialGas The amount of gas before any modifier execution.
function _metered(uint64 _amount, uint256 _initialGas) internal {
// Update block number and base fee if necessary.
uint256 blockDiff = block.number - params.prevBlockNum;
ResourceConfig memory config = _resourceConfig();
int256 targetResourceLimit = int256(uint256(config.maxResourceLimit)) /
int256(uint256(config.elasticityMultiplier));
if (blockDiff > 0) {
// Handle updating EIP-1559 style gas parameters. We use EIP-1559 to restrict the rate
// at which deposits can be created and therefore limit the potential for deposits to
// spam the L2 system. Fee scheme is very similar to EIP-1559 with minor changes.
int256 gasUsedDelta = int256(uint256(params.prevBoughtGas)) -
targetResourceLimit;
int256 baseFeeDelta = (int256(uint256(params.prevBaseFee)) *
gasUsedDelta) /
(targetResourceLimit *
int256(uint256(config.baseFeeMaxChangeDenominator)));
// Update base fee by adding the base fee delta and clamp the resulting value between
// min and max.
int256 newBaseFee = Arithmetic.clamp({
_value: int256(uint256(params.prevBaseFee)) + baseFeeDelta,
_min: int256(uint256(config.minimumBaseFee)),
_max: int256(uint256(config.maximumBaseFee))
});
// If we skipped more than one block, we also need to account for every empty block.
// Empty block means there was no demand for deposits in that block, so we should
// reflect this lack of demand in the fee.
if (blockDiff > 1) {
// Update the base fee by repeatedly applying the exponent 1-(1/change_denominator)
// blockDiff - 1 times. Simulates multiple empty blocks. Clamp the resulting value
// between min and max.
newBaseFee = Arithmetic.clamp({
_value: Arithmetic.cdexp({
_coefficient: newBaseFee,
_denominator: int256(
uint256(config.baseFeeMaxChangeDenominator)
),
_exponent: int256(blockDiff - 1)
}),
_min: int256(uint256(config.minimumBaseFee)),
_max: int256(uint256(config.maximumBaseFee))
});
}
// Update new base fee, reset bought gas, and update block number.
params.prevBaseFee = uint128(uint256(newBaseFee));
params.prevBoughtGas = 0;
params.prevBlockNum = uint64(block.number);
}
// Make sure we can actually buy the resource amount requested by the user.
params.prevBoughtGas += _amount;
if (
int256(uint256(params.prevBoughtGas)) >
int256(uint256(config.maxResourceLimit))
) {
revert OutOfGas();
}
// Determine the amount of ETH to be paid.
uint256 resourceCost = uint256(_amount) * uint256(params.prevBaseFee);
// We currently charge for this ETH amount as an L1 gas burn, so we convert the ETH amount
// into gas by dividing by the L1 base fee. We assume a minimum base fee of 1 gwei to avoid
// division by zero for L1s that don't support 1559 or to avoid excessive gas burns during
// periods of extremely low L1 demand. One-day average gas fee hasn't dipped below 1 gwei
// during any 1 day period in the last 5 years, so should be fine.
uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei);
// Give the user a refund based on the amount of gas they used to do all of the work up to
// this point. Since we're at the end of the modifier, this should be pretty accurate. Acts
// effectively like a dynamic stipend (with a minimum value).
uint256 usedGas = _initialGas - gasleft();
if (gasCost > usedGas) {
Burn.gas(gasCost - usedGas);
}
}
/// @notice Adds an amount of L2 gas consumed to the prev bought gas params. This is meant to be used
/// when L2 system transactions are generated from L1.
/// @param _amount Amount of the L2 gas resource requested.
function useGas(uint32 _amount) internal {
params.prevBoughtGas += uint64(_amount);
}
/// @notice Virtual function that returns the resource config.
/// Contracts that inherit this contract must implement this function.
/// @return ResourceConfig
function _resourceConfig() internal virtual returns (ResourceConfig memory);
/// @notice Sets initial resource parameter values.
/// This function must either be called by the initializer function of an upgradeable
/// child contract.
function __ResourceMetering_init() internal onlyInitializing {
if (params.prevBlockNum == 0) {
params = ResourceParams({
prevBaseFee: 1 gwei,
prevBoughtGas: 0,
prevBlockNum: uint64(block.number)
});
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import {Constants} from "../libraries/Constants.sol";
import {GasPayingToken, IGasToken} from "../libraries/GasPayingToken.sol";
import "../libraries/L1BlockErrors.sol";
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title L1Block
/// @notice The L1Block predeploy gives users access to information about the last known L1 block.
/// Values within this contract are updated once per epoch (every L1 block) and can only be
/// set by the "depositor" account, a special system address. Depositor account transactions
/// are created by the protocol whenever we move to a new epoch.
contract L1Block is IGasToken {
/// @notice Event emitted when the gas paying token is set.
event GasPayingTokenSet(
address indexed token,
uint8 indexed decimals,
bytes32 name,
bytes32 symbol
);
/// @notice Address of the special depositor account.
function DEPOSITOR_ACCOUNT() public pure returns (address addr_) {
addr_ = Constants.DEPOSITOR_ACCOUNT;
}
/// @notice The latest L1 block number known by the L2 system.
uint64 public number;
/// @notice The latest L1 timestamp known by the L2 system.
uint64 public timestamp;
/// @notice The latest L1 base fee.
uint256 public basefee;
/// @notice The latest L1 blockhash.
bytes32 public hash;
/// @notice The number of L2 blocks in the same epoch.
uint64 public sequenceNumber;
/// @notice The scalar value applied to the L1 blob base fee portion of the blob-capable L1 cost func.
uint32 public blobBaseFeeScalar;
/// @notice The scalar value applied to the L1 base fee portion of the blob-capable L1 cost func.
uint32 public baseFeeScalar;
/// @notice The versioned hash to authenticate the batcher by.
bytes32 public batcherHash;
/// @notice The overhead value applied to the L1 portion of the transaction fee.
/// @custom:legacy
uint256 public l1FeeOverhead;
/// @notice The scalar value applied to the L1 portion of the transaction fee.
/// @custom:legacy
uint256 public l1FeeScalar;
/// @notice The latest L1 blob base fee.
uint256 public blobBaseFee;
/// @custom:semver 1.4.1-beta.1
function version() public pure virtual returns (string memory) {
return "1.4.1-beta.1";
}
/// @notice Returns the gas paying token, its decimals, name and symbol.
/// If nothing is set in state, then it means ether is used.
function gasPayingToken()
public
view
returns (address addr_, uint8 decimals_)
{
(addr_, decimals_) = GasPayingToken.getToken();
}
/// @notice Returns the gas paying token name.
/// If nothing is set in state, then it means ether is used.
function gasPayingTokenName() public view returns (string memory name_) {
name_ = GasPayingToken.getName();
}
/// @notice Returns the gas paying token symbol.
/// If nothing is set in state, then it means ether is used.
function gasPayingTokenSymbol()
public
view
returns (string memory symbol_)
{
symbol_ = GasPayingToken.getSymbol();
}
/// @notice Getter for custom gas token paying networks. Returns true if the
/// network uses a custom gas token.
function isCustomGasToken() public view returns (bool) {
(address token, ) = gasPayingToken();
return token != Constants.ETHER;
}
/// @custom:legacy
/// @notice Updates the L1 block values.
/// @param _number L1 blocknumber.
/// @param _timestamp L1 timestamp.
/// @param _basefee L1 basefee.
/// @param _hash L1 blockhash.
/// @param _sequenceNumber Number of L2 blocks since epoch start.
/// @param _batcherHash Versioned hash to authenticate batcher by.
/// @param _l1FeeOverhead L1 fee overhead.
/// @param _l1FeeScalar L1 fee scalar.
function setL1BlockValues(
uint64 _number,
uint64 _timestamp,
uint256 _basefee,
bytes32 _hash,
uint64 _sequenceNumber,
bytes32 _batcherHash,
uint256 _l1FeeOverhead,
uint256 _l1FeeScalar
) external {
require(
msg.sender == DEPOSITOR_ACCOUNT(),
"L1Block: only the depositor account can set L1 block values"
);
number = _number;
timestamp = _timestamp;
basefee = _basefee;
hash = _hash;
sequenceNumber = _sequenceNumber;
batcherHash = _batcherHash;
l1FeeOverhead = _l1FeeOverhead;
l1FeeScalar = _l1FeeScalar;
}
/// @notice Updates the L1 block values for an Ecotone upgraded chain.
/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
/// Params are expected to be in the following order:
/// 1. _baseFeeScalar L1 base fee scalar
/// 2. _blobBaseFeeScalar L1 blob base fee scalar
/// 3. _sequenceNumber Number of L2 blocks since epoch start.
/// 4. _timestamp L1 timestamp.
/// 5. _number L1 blocknumber.
/// 6. _basefee L1 base fee.
/// 7. _blobBaseFee L1 blob base fee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
function setL1BlockValuesEcotone() external {
address depositor = DEPOSITOR_ACCOUNT();
assembly {
// Revert if the caller is not the depositor account.
if xor(caller(), depositor) {
mstore(0x00, 0x3cc50b45) // 0x3cc50b45 is the 4-byte selector of "NotDepositor()"
revert(0x1C, 0x04) // returns the stored 4-byte selector from above
}
// sequencenum (uint64), blobBaseFeeScalar (uint32), baseFeeScalar (uint32)
sstore(sequenceNumber.slot, shr(128, calldataload(4)))
// number (uint64) and timestamp (uint64)
sstore(number.slot, shr(128, calldataload(20)))
sstore(basefee.slot, calldataload(36)) // uint256
sstore(blobBaseFee.slot, calldataload(68)) // uint256
sstore(hash.slot, calldataload(100)) // bytes32
sstore(batcherHash.slot, calldataload(132)) // bytes32
}
}
/// @notice Sets the gas paying token for the L2 system. Can only be called by the special
/// depositor account. This function is not called on every L2 block but instead
/// only called by specially crafted L1 deposit transactions.
function setGasPayingToken(
address _token,
uint8 _decimals,
bytes32 _name,
bytes32 _symbol
) external {
if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor();
GasPayingToken.set({
_token: _token,
_decimals: _decimals,
_name: _name,
_symbol: _symbol
});
emit GasPayingTokenSet({
token: _token,
decimals: _decimals,
name: _name,
symbol: _symbol
});
}
}// 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.22;
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.0;
import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol";
import {FixedPointMathLib} from "./FixedPointMathLib.sol";
/// @title Arithmetic
/// @notice Even more math than before.
library Arithmetic {
/// @notice Clamps a value between a minimum and maximum.
/// @param _value The value to clamp.
/// @param _min The minimum value.
/// @param _max The maximum value.
/// @return The clamped value.
function clamp(
int256 _value,
int256 _min,
int256 _max
) internal pure returns (int256) {
return SignedMath.min(SignedMath.max(_value, _min), _max);
}
/// @notice (c)oefficient (d)enominator (exp)onentiation function.
/// Returns the result of: c * (1 - 1/d)^exp.
/// @param _coefficient Coefficient of the function.
/// @param _denominator Fractional denominator.
/// @param _exponent Power function exponent.
/// @return Result of c * (1 - 1/d)^exp.
function cdexp(
int256 _coefficient,
int256 _denominator,
int256 _exponent
) internal pure returns (int256) {
return
(_coefficient *
(
FixedPointMathLib.powWad(
1e18 - (1e18 / _denominator),
_exponent * 1e18
)
)) / 1e18;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
/// @title Burn
/// @notice Utilities for burning stuff.
library Burn {
/// @notice Burns a given amount of ETH.
/// @param _amount Amount of ETH to burn.
function eth(uint256 _amount) internal {
new Burner{ value: _amount }();
}
/// @notice Burns a given amount of gas.
/// @param _amount Amount of gas to burn.
function gas(uint256 _amount) internal view {
uint256 i = 0;
uint256 initialGas = gasleft();
while (initialGas - gasleft() < _amount) {
++i;
}
}
}
/// @title Burner
/// @notice Burner self-destructs on creation and sends all ETH to itself, removing all ETH given to
/// the contract from the circulating supply. Self-destructing is the only way to remove ETH
/// from the circulating supply.
contract Burner {
constructor() payable {
selfdestruct(payable(address(this)));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
/// @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.0;
import {ResourceMetering} from "../L1/ResourceMetering.sol";
/// @title Constants
/// @notice Constants is a library for storing constants. Simple! Don't put everything in here, just
/// the stuff used in multiple contracts. Constants that only apply to a single contract
/// should be defined in that contract instead.
library Constants {
/// @notice Special address to be used as the tx origin for gas estimation calls in the
/// OptimismPortal and CrossDomainMessenger calls. You only need to use this address if
/// the minimum gas limit specified by the user is not actually enough to execute the
/// given message and you're attempting to estimate the actual necessary gas limit. We
/// use address(1) because it's the ecrecover precompile and therefore guaranteed to
/// never have any code on any EVM chain.
address internal constant ESTIMATION_ADDRESS = address(1);
/// @notice Value used for the L2 sender storage slot in both the OptimismPortal and the
/// CrossDomainMessenger contracts before an actual sender is set. This value is
/// non-zero to reduce the gas cost of message passing transactions.
address internal constant DEFAULT_L2_SENDER =
0x000000000000000000000000000000000000dEaD;
/// @notice The storage slot that holds the address of a proxy implementation.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
bytes32 internal constant PROXY_OWNER_ADDRESS =
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice The address that represents ether when dealing with ERC20 token addresses.
address internal constant ETHER =
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @notice The address that represents the system caller responsible for L1 attributes
/// transactions.
address internal constant DEPOSITOR_ACCOUNT =
0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
/// @notice Returns the default values for the ResourceConfig. These are the recommended values
/// for a production network.
function DEFAULT_RESOURCE_CONFIG()
internal
pure
returns (ResourceMetering.ResourceConfig memory)
{
ResourceMetering.ResourceConfig memory config = ResourceMetering
.ResourceConfig({
maxResourceLimit: 20_000_000,
elasticityMultiplier: 10,
baseFeeMaxChangeDenominator: 8,
minimumBaseFee: 1 gwei,
systemTxMaxGas: 1_000_000,
maximumBaseFee: type(uint128).max
});
return config;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Types } from "./Types.sol";
import { Hashing } from "./Hashing.sol";
import { RLPWriter } from "./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)
{
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)
{
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.
/// @param _dependencySet Array of the chain IDs in the interop dependency set.
function encodeSetL1BlockValuesInterop(
uint32 _baseFeeScalar,
uint32 _blobBaseFeeScalar,
uint64 _sequenceNumber,
uint64 _timestamp,
uint64 _number,
uint256 _baseFee,
uint256 _blobBaseFee,
bytes32 _hash,
bytes32 _batcherHash,
uint256[] memory _dependencySet
)
internal
pure
returns (bytes memory)
{
require(_dependencySet.length <= type(uint8).max, "Encoding: dependency set length is too large");
// Check that the batcher hash is just the address with 0 padding to the left for version 0.
require(uint160(uint256(_batcherHash)) == uint256(_batcherHash), "Encoding: invalid batcher hash");
bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesInterop()"));
return abi.encodePacked(
functionSignature,
_baseFeeScalar,
_blobBaseFeeScalar,
_sequenceNumber,
_timestamp,
_number,
_baseFee,
_blobBaseFee,
_hash,
_batcherHash,
uint8(_dependencySet.length),
_dependencySet
);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)
return expWad((lnWad(x) * y) / int256(WAD)); // Using ln(x) means x must be greater than 0.
}
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is < 0.5 we return zero. This happens when
// x <= floor(log(0.5e18) * 1e18) ~ -42e18
if (x <= -42139678854452767551) return 0;
// When the result is > (2**255 - 1) / 1e18 we can not represent it as an
// int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
if (x >= 135305999368893231589) revert("EXP_OVERFLOW");
// x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >>
96;
x = x - k * 54916777467707473351141471128;
// k is in the range [-61, 195].
// Evaluate using a (6, 7)-term rational approximation.
// p is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already 2**96 too large.
r := sdiv(p, q)
}
// r should be in the range (0.09, 0.25) * 2**96.
// We now need to multiply r by:
// * the scale factor s = ~6.031367120.
// * the 2**k factor from the range reduction.
// * the 1e18 / 2**96 factor for base conversion.
// We do this all at once, with an intermediate result in 2**213
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) *
3822833074963236453042738258902158003155416615667) >>
uint256(195 - k)
);
}
}
function lnWad(int256 x) internal pure returns (int256 r) {
unchecked {
require(x > 0, "UNDEFINED");
// We want to convert x from 10**18 fixed point to 2**96 fixed point.
// We do this by multiplying by 2**96 / 10**18. But since
// ln(x * C) = ln(x) + ln(C), we can simply do nothing here
// and add ln(2**96 / 10**18) at the end.
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
int256 k = int256(log2(uint256(x))) - 96;
x <<= uint256(159 - k);
x = int256(uint256(x) >> 159);
// Evaluate using a (8, 8)-term rational approximation.
// p is made monic, we will multiply by a scale factor later.
int256 p = x + 3273285459638523848632254066296;
p = ((p * x) >> 96) + 24828157081833163892658089445524;
p = ((p * x) >> 96) + 43456485725739037958740375743393;
p = ((p * x) >> 96) - 11111509109440967052023855526967;
p = ((p * x) >> 96) - 45023709667254063763336534515857;
p = ((p * x) >> 96) - 14706773417378608786704636184526;
p = p * x - (795164235651350426258249787498 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
// q is monic by convention.
int256 q = x + 5573035233440673466300451813936;
q = ((q * x) >> 96) + 71694874799317883764090561454958;
q = ((q * x) >> 96) + 283447036172924575727196451306956;
q = ((q * x) >> 96) + 401686690394027663651624208769553;
q = ((q * x) >> 96) + 204048457590392012362485061816622;
q = ((q * x) >> 96) + 31853899698501571402653359427138;
q = ((q * x) >> 96) + 909429971244387300277376558375;
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already 2**96 too large.
r := sdiv(p, q)
}
// r is in the range (0, 0.125) * 2**96
// Finalization, we need to:
// * multiply by the scale factor s = 5.549…
// * add ln(2**96 / 10**18)
// * add k * ln(2)
// * multiply by 10**18 / 2**96 = 5**18 >> 78
// mul s * 5e18 * 2**96, base is now 5**18 * 2**192
r *= 1677202110996718588342820967067443963516166;
// add ln(2) * k * 5e18 * 2**192
r +=
16597577552685614221487285958193947469193820559219878177908093499208371 *
k;
// add ln(2**96 / 10**18) * 5e18 * 2**192
r += 600920179829731861736702779321621459595472258049074101567377883020018308;
// base conversion: mul 2**18 / 2**192
r >>= 174;
}
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(
and(
iszero(iszero(denominator)),
or(iszero(x), eq(div(z, x), y))
)
) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(
and(
iszero(iszero(denominator)),
or(iszero(x), eq(div(z, x), y))
)
) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.
// We allow z - 1 to underflow if z is 0, because we multiply the
// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function log2(uint256 x) internal pure returns (uint256 r) {
require(x > 0, "UNDEFINED");
assembly {
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(r, shl(3, lt(0xff, shr(r, x))))
r := or(r, shl(2, lt(0xf, shr(r, x))))
r := or(r, shl(1, lt(0x3, shr(r, x))))
r := or(r, lt(0x1, shr(r, x)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Storage} from "./Storage.sol";
import {Constants} from "./Constants.sol";
import {LibString} from "solady/src/utils/LibString.sol";
/// @title IGasToken
/// @notice Implemented by contracts that are aware of the custom gas token used
/// by the L2 network.
interface IGasToken {
/// @notice Getter for the ERC20 token address that is used to pay for gas and its decimals.
function gasPayingToken() external view returns (address, uint8);
/// @notice Returns the gas token name.
function gasPayingTokenName() external view returns (string memory);
/// @notice Returns the gas token symbol.
function gasPayingTokenSymbol() external view returns (string memory);
/// @notice Returns true if the network uses a custom gas token.
function isCustomGasToken() external view returns (bool);
}
/// @title GasPayingToken
/// @notice Handles reading and writing the custom gas token to storage.
/// To be used in any place where gas token information is read or
/// written to state. If multiple contracts use this library, the
/// values in storage should be kept in sync between them.
library GasPayingToken {
/// @notice The storage slot that contains the address and decimals of the gas paying token
bytes32 internal constant GAS_PAYING_TOKEN_SLOT =
bytes32(uint256(keccak256("opstack.gaspayingtoken")) - 1);
/// @notice The storage slot that contains the ERC20 `name()` of the gas paying token
bytes32 internal constant GAS_PAYING_TOKEN_NAME_SLOT =
bytes32(uint256(keccak256("opstack.gaspayingtokenname")) - 1);
/// @notice the storage slot that contains the ERC20 `symbol()` of the gas paying token
bytes32 internal constant GAS_PAYING_TOKEN_SYMBOL_SLOT =
bytes32(uint256(keccak256("opstack.gaspayingtokensymbol")) - 1);
/// @notice Reads the gas paying token and its decimals from the magic
/// storage slot. If nothing is set in storage, then the ether
/// address is returned instead.
function getToken() internal view returns (address addr_, uint8 decimals_) {
bytes32 slot = Storage.getBytes32(GAS_PAYING_TOKEN_SLOT);
addr_ = address(uint160(uint256(slot) & uint256(type(uint160).max)));
if (addr_ == address(0)) {
addr_ = Constants.ETHER;
decimals_ = 18;
} else {
decimals_ = uint8(uint256(slot) >> 160);
}
}
/// @notice Reads the gas paying token's name from the magic storage slot.
/// If nothing is set in storage, then the ether name, 'Ether', is returned instead.
function getName() internal view returns (string memory name_) {
(address addr, ) = getToken();
if (addr == Constants.ETHER) {
name_ = "Ether";
} else {
name_ = LibString.fromSmallString(
Storage.getBytes32(GAS_PAYING_TOKEN_NAME_SLOT)
);
}
}
/// @notice Reads the gas paying token's symbol from the magic storage slot.
/// If nothing is set in storage, then the ether symbol, 'ETH', is returned instead.
function getSymbol() internal view returns (string memory symbol_) {
(address addr, ) = getToken();
if (addr == Constants.ETHER) {
symbol_ = "ETH";
} else {
symbol_ = LibString.fromSmallString(
Storage.getBytes32(GAS_PAYING_TOKEN_SYMBOL_SLOT)
);
}
}
/// @notice Writes the gas paying token, its decimals, name and symbol to the magic storage slot.
function set(
address _token,
uint8 _decimals,
bytes32 _name,
bytes32 _symbol
) internal {
Storage.setBytes32(
GAS_PAYING_TOKEN_SLOT,
bytes32((uint256(_decimals) << 160) | uint256(uint160(_token)))
);
Storage.setBytes32(GAS_PAYING_TOKEN_NAME_SLOT, _name);
Storage.setBytes32(GAS_PAYING_TOKEN_SYMBOL_SLOT, _symbol);
}
/// @notice Maps a string to a normalized null-terminated small string.
function sanitize(string memory _str) internal pure returns (bytes32) {
require(
bytes(_str).length <= 32,
"GasPayingToken: string cannot be greater than 32 bytes"
);
return LibString.toSmallString(_str);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Types } from "./Types.sol";
import { Encoding } from "./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
)
);
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @notice Error returns when a non-depositor account tries to set L1 block values. error NotDepositor(); /// @notice Error when a chain ID is not in the interop dependency set. error NotDependency(); /// @notice Error when the interop dependency set size is too large. error DependencySetSizeTooLarge(); /// @notice Error when a chain ID already in the interop dependency set is attempted to be added. error AlreadyDependency(); /// @notice Error when the chain's chain ID is attempted to be removed from the interop dependency set. error CantRemovedDependency();
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import { Bytes } from "./Bytes.sol";
import { RLPReader } from "./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_;
}
}
}
}pragma solidity 0.8.22;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
constructor(address pauser) Ownable(pauser) {}
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() public onlyOwner whenNotPaused {
paused = true;
emit Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() public onlyOwner whenPaused {
paused = false;
emit Unpause();
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @notice Error for when a deposit or withdrawal is to a bad target. error BadTarget(); /// @notice Error for when a deposit has too much calldata. error LargeCalldata(); /// @notice Error for when a deposit has too small of a gas limit. error SmallGasLimit(); /// @notice Error for when a withdrawal transfer fails. error TransferFailed(); /// @notice Error for when a method is called that only works when using a custom gas token. error OnlyCustomGasToken(); /// @notice Error for when a method cannot be called with non zero CALLVALUE. error NoValue(); /// @notice Error for an unauthorized CALLER. error Unauthorized(); /// @notice Error for when a method cannot be called when paused. This could be renamed /// to `Paused` in the future, but it collides with the `Paused` event. error CallPaused(); /// @notice Error for special gas estimation. error GasEstimation(); /// @notice Error for when a method is being reentered. error NonReentrant();
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Predeploys
/// @notice Contains constant addresses for protocol contracts that are pre-deployed to the L2 system.
// This excludes the preinstalls (non-protocol contracts).
library Predeploys {
/// @notice Number of predeploy-namespace addresses reserved for protocol usage.
uint256 internal constant PREDEPLOY_COUNT = 2048;
/// @notice Address of the L2CrossDomainMessenger predeploy.
address internal constant L2_CROSS_DOMAIN_MESSENGER =
0x4200000000000000000000000000000000000007;
/// @notice Address of the L2StandardBridge predeploy.
address internal constant L2_STANDARD_BRIDGE =
0x4200000000000000000000000000000000000010;
/// @notice Address of the LightLinkMintableERC20Factory predeploy.
address internal constant LIGHTLINK_MINTABLE_ERC20_FACTORY =
0x4200000000000000000000000000000000000012;
/// @notice Address of the L1Block predeploy.
address internal constant L1_BLOCK_ATTRIBUTES =
0x4200000000000000000000000000000000000015;
/// @notice Address of the L2ToL1MessagePasser predeploy.
address internal constant L2_TO_L1_MESSAGE_PASSER =
0x4200000000000000000000000000000000000016;
/// @notice Address of the ProxyAdmin predeploy.
address internal constant PROXY_ADMIN =
0x4200000000000000000000000000000000000018;
/// @notice Returns the name of the predeploy at the given address.
function getName(address _addr) internal pure returns (string memory out_) {
require(
isPredeployNamespace(_addr),
"Predeploys: address must be a predeploy"
);
if (_addr == L2_CROSS_DOMAIN_MESSENGER) return "L2CrossDomainMessenger";
if (_addr == L2_STANDARD_BRIDGE) return "L2StandardBridge";
if (_addr == LIGHTLINK_MINTABLE_ERC20_FACTORY)
return "LightLinkMintableERC20Factory";
if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block";
if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser";
if (_addr == PROXY_ADMIN) return "ProxyAdmin";
revert("Predeploys: unnamed predeploy");
}
/// @notice Returns true if the predeploy is not proxied.
function notProxied(address _addr) internal pure returns (bool) {
return false;
}
/// @notice Returns true if the address is a defined predeploy that is embedded into new OP-Stack chains.
function isSupportedPredeploy(address _addr) internal pure returns (bool) {
return
_addr == L2_CROSS_DOMAIN_MESSENGER ||
_addr == L2_STANDARD_BRIDGE ||
_addr == LIGHTLINK_MINTABLE_ERC20_FACTORY ||
_addr == L1_BLOCK_ATTRIBUTES ||
_addr == L2_TO_L1_MESSAGE_PASSER ||
_addr == PROXY_ADMIN;
}
function isPredeployNamespace(address _addr) internal pure returns (bool) {
return
uint160(_addr) >> 11 ==
uint160(0x4200000000000000000000000000000000000000) >> 11;
}
/// @notice Function to compute the expected address of the predeploy implementation
/// in the genesis state.
function predeployToCodeNamespace(
address _addr
) internal pure returns (address) {
require(
isPredeployNamespace(_addr),
"Predeploys: can only derive code-namespace address for predeploy addresses"
);
return
address(
uint160(
(uint256(uint160(_addr)) & 0xffff) |
uint256(
uint160(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000)
)
)
);
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @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();
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "./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
pragma solidity 0.8.22;
/// @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.22;
/// @title SafeCall
/// @notice Perform low level safe calls
library SafeCall {
/// @notice Performs a low level call without copying any returndata.
/// @dev Passes no calldata to the call context.
/// @param _target Address to call
/// @param _gas Amount of gas to pass to the call
/// @param _value Amount of value to pass to the call
function send(address _target, uint256 _gas, uint256 _value) internal returns (bool success_) {
assembly {
success_ :=
call(
_gas, // gas
_target, // recipient
_value, // ether value
0, // inloc
0, // inlen
0, // outloc
0 // outlen
)
}
}
/// @notice Perform a low level call with all gas without copying any returndata
/// @param _target Address to call
/// @param _value Amount of value to pass to the call
function send(address _target, uint256 _value) internal returns (bool success_) {
success_ = send(_target, gasleft(), _value);
}
/// @notice Perform a low level call without copying any returndata
/// @param _target Address to call
/// @param _gas Amount of gas to pass to the call
/// @param _value Amount of value to pass to the call
/// @param _calldata Calldata to pass to the call
function call(
address _target,
uint256 _gas,
uint256 _value,
bytes memory _calldata
)
internal
returns (bool success_)
{
assembly {
success_ :=
call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 32), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
}
}
/// @notice Helper function to determine if there is sufficient gas remaining within the context
/// to guarantee that the minimum gas requirement for a call will be met as well as
/// optionally reserving a specified amount of gas for after the call has concluded.
/// @param _minGas The minimum amount of gas that may be passed to the target context.
/// @param _reservedGas Optional amount of gas to reserve for the caller after the execution
/// of the target context.
/// @return `true` if there is enough gas remaining to safely supply `_minGas` to the target
/// context as well as reserve `_reservedGas` for the caller after the execution of
/// the target context.
/// @dev !!!!! FOOTGUN ALERT !!!!!
/// 1.) The 40_000 base buffer is to account for the worst case of the dynamic cost of the
/// `CALL` opcode's `address_access_cost`, `positive_value_cost`, and
/// `value_to_empty_account_cost` factors with an added buffer of 5,700 gas. It is
/// still possible to self-rekt by initiating a withdrawal with a minimum gas limit
/// that does not account for the `memory_expansion_cost` & `code_execution_cost`
/// factors of the dynamic cost of the `CALL` opcode.
/// 2.) This function should *directly* precede the external call if possible. There is an
/// added buffer to account for gas consumed between this check and the call, but it
/// is only 5,700 gas.
/// 3.) Because EIP-150 ensures that a maximum of 63/64ths of the remaining gas in the call
/// frame may be passed to a subcontext, we need to ensure that the gas will not be
/// truncated.
/// 4.) Use wisely. This function is not a silver bullet.
function hasMinGas(uint256 _minGas, uint256 _reservedGas) internal view returns (bool) {
bool _hasMinGas;
assembly {
// Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas)
_hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63))))
}
return _hasMinGas;
}
/// @notice Perform a low level call without copying any returndata. This function
/// will revert if the call cannot be performed with the specified minimum
/// gas.
/// @param _target Address to call
/// @param _minGas The minimum amount of gas that may be passed to the call
/// @param _value Amount of value to pass to the call
/// @param _calldata Calldata to pass to the call
function callWithMinGas(
address _target,
uint256 _minGas,
uint256 _value,
bytes memory _calldata
)
internal
returns (bool)
{
bool _success;
bool _hasMinGas = hasMinGas(_minGas, 0);
assembly {
// Assertion: gasleft() >= (_minGas * 64) / 63 + 40_000
if iszero(_hasMinGas) {
// Store the "Error(string)" selector in scratch space.
mstore(0, 0x08c379a0)
// Store the pointer to the string length in scratch space.
mstore(32, 32)
// Store the string.
//
// SAFETY:
// - We pad the beginning of the string with two zero bytes as well as the
// length (24) to ensure that we override the free memory pointer at offset
// 0x40. This is necessary because the free memory pointer is likely to
// be greater than 1 byte when this function is called, but it is incredibly
// unlikely that it will be greater than 3 bytes. As for the data within
// 0x60, it is ensured that it is 0 due to 0x60 being the zero offset.
// - It's fine to clobber the free memory pointer, we're reverting.
mstore(88, 0x0000185361666543616c6c3a204e6f7420656e6f75676820676173)
// Revert with 'Error("SafeCall: Not enough gas")'
revert(28, 100)
}
// The call will be supplied at least ((_minGas * 64) / 63) gas due to the
// above assertion. This ensures that, in all circumstances (except for when the
// `_minGas` does not account for the `memory_expansion_cost` and `code_execution_cost`
// factors of the dynamic cost of the `CALL` opcode), the call will receive at least
// the minimum amount of gas specified.
_success :=
call(
gas(), // gas
_target, // recipient
_value, // ether value
add(_calldata, 32), // inloc
mload(_calldata), // inlen
0x00, // outloc
0x00 // outlen
)
}
return _success;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import { MerkleTrie } from "./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;
/// @title Storage
/// @notice Storage handles reading and writing to arbitary storage locations
library Storage {
/// @notice Returns an address stored in an arbitrary storage slot.
/// These storage slots decouple the storage layout from
/// solc's automation.
/// @param _slot The storage slot to retrieve the address from.
function getAddress(bytes32 _slot) internal view returns (address addr_) {
assembly {
addr_ := sload(_slot)
}
}
/// @notice Stores an address in an arbitrary storage slot, `_slot`.
/// @param _slot The storage slot to store the address in.
/// @param _address The protocol version to store
/// @dev WARNING! This function must be used cautiously, as it allows for overwriting addresses
/// in arbitrary storage slots.
function setAddress(bytes32 _slot, address _address) internal {
assembly {
sstore(_slot, _address)
}
}
/// @notice Returns a uint256 stored in an arbitrary storage slot.
/// These storage slots decouple the storage layout from
/// solc's automation.
/// @param _slot The storage slot to retrieve the address from.
function getUint(bytes32 _slot) internal view returns (uint256 value_) {
assembly {
value_ := sload(_slot)
}
}
/// @notice Stores a value in an arbitrary storage slot, `_slot`.
/// @param _slot The storage slot to store the address in.
/// @param _value The protocol version to store
/// @dev WARNING! This function must be used cautiously, as it allows for overwriting values
/// in arbitrary storage slots.
function setUint(bytes32 _slot, uint256 _value) internal {
assembly {
sstore(_slot, _value)
}
}
/// @notice Returns a bytes32 stored in an arbitrary storage slot.
/// These storage slots decouple the storage layout from
/// solc's automation.
/// @param _slot The storage slot to retrieve the address from.
function getBytes32(bytes32 _slot) internal view returns (bytes32 value_) {
assembly {
value_ := sload(_slot)
}
}
/// @notice Stores a bytes32 value in an arbitrary storage slot, `_slot`.
/// @param _slot The storage slot to store the address in.
/// @param _value The bytes32 value to store.
/// @dev WARNING! This function must be used cautiously, as it allows for overwriting values
/// in arbitrary storage slots.
function setBytes32(bytes32 _slot, bytes32 _value) internal {
assembly {
sstore(_slot, _value)
}
}
/// @notice Stores a bool value in an arbitrary storage slot, `_slot`.
/// @param _slot The storage slot to store the bool in.
/// @param _value The bool value to store
/// @dev WARNING! This function must be used cautiously, as it allows for overwriting values
/// in arbitrary storage slots.
function setBool(bytes32 _slot, bool _value) internal {
assembly {
sstore(_slot, _value)
}
}
/// @notice Returns a bool stored in an arbitrary storage slot.
/// @param _slot The storage slot to retrieve the bool from.
function getBool(bytes32 _slot) internal view returns (bool value_) {
assembly {
value_ := sload(_slot)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
/// @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;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, 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(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"BadTarget","type":"error"},{"inputs":[],"name":"ContentLengthMismatch","type":"error"},{"inputs":[],"name":"EmptyItem","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"GasEstimation","type":"error"},{"inputs":[],"name":"InvalidDataRemainder","type":"error"},{"inputs":[],"name":"InvalidHeader","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"LargeCalldata","type":"error"},{"inputs":[],"name":"NoValue","type":"error"},{"inputs":[],"name":"NonReentrant","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyCustomGasToken","type":"error"},{"inputs":[],"name":"OutOfGas","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SmallGasLimit","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UnexpectedList","type":"error"},{"inputs":[],"name":"UnexpectedString","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"opaqueData","type":"bytes"}],"name":"TransactionDeposited","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"withdrawalHash","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"WithdrawalFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"withdrawalHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"WithdrawalProven","type":"event"},{"inputs":[],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_mint","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint64","name":"_gasLimit","type":"uint64"},{"internalType":"bool","name":"_isCreation","type":"bool"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"depositERC20Transaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint64","name":"_gasLimit","type":"uint64"},{"internalType":"bool","name":"_isCreation","type":"bool"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"depositTransaction","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"donateETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Types.WithdrawalTransaction","name":"_tx","type":"tuple"}],"name":"finalizeWithdrawalTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"finalizedWithdrawals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ICanonicalStateChain","name":"_l2Oracle","type":"address"},{"internalType":"contract IChallengeBase","name":"_challenge","type":"address"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_l2OutputIndex","type":"uint256"}],"name":"isOutputFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2Oracle","outputs":[{"internalType":"contract ICanonicalStateChain","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2Sender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"_byteCount","type":"uint64"}],"name":"minimumGasLimit","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"internalType":"uint128","name":"prevBaseFee","type":"uint128"},{"internalType":"uint64","name":"prevBoughtGas","type":"uint64"},{"internalType":"uint64","name":"prevBlockNum","type":"uint64"}],"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":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Types.WithdrawalTransaction","name":"_tx","type":"tuple"},{"internalType":"uint256","name":"_l2OutputIndex","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":"_outputRootProof","type":"tuple"},{"internalType":"bytes[]","name":"_withdrawalProof","type":"bytes[]"}],"name":"proveWithdrawalTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"provenWithdrawals","outputs":[{"internalType":"bytes32","name":"outputRoot","type":"bytes32"},{"internalType":"uint128","name":"timestamp","type":"uint128"},{"internalType":"uint128","name":"l2OutputIndex","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resourceConfig","outputs":[{"internalType":"uint32","name":"maxResourceLimit","type":"uint32"},{"internalType":"uint8","name":"elasticityMultiplier","type":"uint8"},{"internalType":"uint8","name":"baseFeeMaxChangeDenominator","type":"uint8"},{"internalType":"uint32","name":"minimumBaseFee","type":"uint32"},{"internalType":"uint32","name":"systemTxMaxGas","type":"uint32"},{"internalType":"uint128","name":"maximumBaseFee","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"bytes32","name":"_name","type":"bytes32"},{"internalType":"bytes32","name":"_symbol","type":"bytes32"}],"name":"setGasPayingToken","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":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
608060409080825234620002e3573315620002cd57506031547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0906001600160a01b03600033828416858380a37ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805490946001600160401b039482881c60ff161594926001600160a81b03199087811680159081620002c4575b6001149081620002b9575b159081620002af575b506200029e576001600160401b0319811660011789558662000280575b50603680546001600160a01b031990811690915560378054821690558951949060c08601898111878210176200026c578b526301312d008652600a60208701526008868c0152633b9aca0060608701819052620f424060808801526001600160801b0360a090970196909652603580546001600160f01b0319166dfff0bdbfc46535fff7f5feced300600160f01b0317905560325487959493929091908216156200025c575b505080331691161760315533908280a384549360ff85881c16156200024b57825460c01c15620001f4575b505050620001b4575b8251613d059081620002e98239f35b68ff0000000000000000191690557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26020825160018152a13880620001a5565b86519060608201828110828211176200023757885282825260208201849052439081169188019190915260c01b6001600160c01b0319161790553880806200019c565b634e487b7160e01b85526041600452602485fd5b8651631afcd79f60e31b8152600490fd5b1661dead17603255388062000171565b634e487b7160e01b88526041600452602488fd5b6001600160481b0319166801000000000000000117885538620000cb565b895163f92ee8a960e01b8152600490fd5b90501538620000ae565b303b159150620000a5565b8891506200009a565b631e4fbdf760e01b815260006004820152602490fd5b600080fdfe60806040526004361015610023575b361561001957600080fd5b610021611d2f565b005b60003560e01c8063149f2f22146101935780633f4ba83a1461018e5780634870496f1461018957806354fd4d50146101845780635c975abb1461017f5780636dbffb781461017a578063715018a61461017557806371cfaa3f146101705780638456cb591461016b5780638b4c40b0146101665780638c3152e9146101615780638da5cb5b1461015c5780639b5f694a146101575780639bf62d8214610152578063a14238e71461014d578063a35d99df14610148578063b69ef8a814610143578063c0c53b8b1461013e578063cc731b0214610139578063cff0ab9614610134578063e965084c1461012f578063e9e05c421461012a5763f2fde38b0361000e57611118565b6110b1565b611068565b611026565b610fc1565b610e73565b610e57565b610e1f565b610dee565b610dc5565b610d9c565b610d73565b61089d565b61088f565b610830565b6106ed565b61068f565b6105d9565b6105b3565b610562565b61049a565b6103ba565b61030a565b6001600160a01b038116036101a957565b600080fd5b6001600160401b038116036101a957565b801515036101a957565b634e487b7160e01b600052604160045260246000fd5b60c081019081106001600160401b038211176101fa57604052565b6101c9565b604081019081106001600160401b038211176101fa57604052565b606081019081106001600160401b038211176101fa57604052565b602081019081106001600160401b038211176101fa57604052565b60a081019081106001600160401b038211176101fa57604052565b90601f801991011681019081106001600160401b038211176101fa57604052565b604051906102998261021a565b565b60405190610299826101ff565b6001600160401b0381116101fa57601f01601f191660200190565b81601f820112156101a9578035906102da826102a8565b926102e8604051948561026b565b828452602083830101116101a957816000926020809301838601378301015290565b346101a95760c03660031901126101a957600480359061032982610198565b602435916064359061033a826101ae565b60843590610347826101bf565b60a4356001600160401b0381116101a95761036590369086016102c3565b945a9473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6103aa565b603855565b61212b565b610396818361026b565b8101906111b2565b503d61038c565b6111c1565b50604051630eaf3c0f60e01b8152fd5b346101a957600080600319360112610418576103d4612310565b60315460ff8160a01c16156104145760ff60a01b19166031557f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b338180a180f35b5080fd5b80fd5b919060c0838203126101a95760405190610434826101df565b819380358352602081013561044881610198565b6020840152604081013561045b81610198565b6040840152606081013560608401526080810135608084015260a0810135916001600160401b0383116101a95760a09261049592016102c3565b910152565b346101a95760e03660031901126101a9576001600160401b036004358181116101a9576104cb90369060040161041b565b9060803660431901126101a95760c435918183116101a957366023840112156101a95782600401359182116101a9573660248360051b850101116101a9576024610021930190602435906111cd565b60005b83811061052d5750506000910152565b818101518382015260200161051d565b906020916105568151809281855285808601910161051a565b601f01601f1916010190565b346101a95760003660031901126101a9576105af604051610582816101ff565b600c81526b322e382e312d626574612e3160a01b602082015260405191829160208352602083019061053d565b0390f35b346101a95760003660031901126101a957602060ff60315460a01c166040519015158152f35b346101a95760203660031901126101a9576036546040805163a25ae55760e01b8152600480359082015291829060249082906001600160a01b03165afa80156103a5576106496001600160401b03602061064e936105af95600091610660575b500151166001600160401b031690565b61242c565b60405190151581529081906020820190565b610682915060403d604011610688575b61067a818361026b565b8101906114f1565b38610639565b503d610670565b346101a957600080600319360112610418576106a9612310565b603180546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b346101a95760803660031901126101a95760043561070a81610198565b60243560ff811681036101a9576107df6107d391600093610729612310565b61076f6107426001600160401b03875460801c16611b50565b6000805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b6040516371cfaa3f60e01b60208201526001600160a01b0391909116602482015260ff9190911660448083019190915235606480830191909152356084808301919091528152601f19906107c460a48261026b565b60405193849160208301611777565b0390810183528261026b565b73deaddeaddeaddeaddeaddeaddeaddeaddead00017fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c326040518061082b6015602160991b0195826117f1565b0390a4005b346101a9576000806003193601126104185761084a612310565b60315460ff8160a01c166104145760ff60a01b1916600160a01b176031557f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff6258180a180f35b60003660031901126101a957005b346101a9576020806003193601126101a9576004906001600160401b039082358281116101a9576108d1903690850161041b565b60ff60315460a01c166101a9576032546001600160a01b039190821661deac1901610d62576108ff8161237d565b9261091c610917856000526034602052604060002090565b6115ca565b818101876001600160801b036109458161093d85516001600160801b031690565b161515611805565b82516001600160801b031660365461096d906001600160a01b03165b6001600160a01b031690565b9160409a878c51809663443c313960e11b825281875afa80156103a557610a07966109b96109d7958f9895610649956109c6956109d2988f600092610d35575b50501691161015611882565b516001600160801b031690565b6001600160801b031690565b611909565b81840151825163a25ae55760e01b81526001600160801b039091168b820190815290938492918391829160200190565b03915afa80156103a557610a468461064992610a5795610a5295600092610d12575b50610a3890825190511461198a565b01516001600160401b031690565b6001600160401b031690565b611a0f565b610a7e610a78610a71866000526033602052604060002090565b5460ff1690565b15611a8d565b610aa2610a95856000526033602052604060002090565b805460ff19166001179055565b80820151603280546001600160a01b0319166001600160a01b0390921691909117905560009273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8403610b8b57505080840151610b2492506001600160a01b031690608081015160a060608301519201519261257b565b905b603280546001600160a01b03191661dead179055825182151581527fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b90602090a21580610b81575b610b7457005b5163eeae4ed360e01b8152fd5b5060013214610b6e565b8286018051909488918190610ba8906001600160a01b0316610961565b14610d03576060850193845180610bfc575b50505050505060a08101519081511515600014610bf1579151608090920151610beb926001600160a01b0316612527565b90610b26565b505050600190610b26565b610382610c0b91603854611b43565b88516370a0823160e01b80825230948201948552938290829081906020010381865afa9283156103a5578b918694610cdc575b50885187519495610c7a9585949392610c6191906001600160a01b0316846124d8565b8c51908152309281019283529485928391829160200190565b03915afa9384156103a55793610cb9575b5050610c9991925190611b43565b03610ca957388086818080610bba565b505050516312171d8360e31b8152fd5b610c9993509081610cd592903d1061039e57610396818361026b565b9138610c8b565b610c7a945090610cfa849392843d861161039e57610396818361026b565b94509091610c3e565b5086516309a4b7ed60e11b8152fd5b610a38919250610d2e908d803d106106885761067a818361026b565b9190610a29565b610d549250803d10610d5b575b610d4c818361026b565b81019061186d565b388f6109ad565b503d610d42565b6040516349cb68ab60e11b81528590fd5b346101a95760003660031901126101a9576031546040516001600160a01b039091168152602090f35b346101a95760003660031901126101a9576036546040516001600160a01b039091168152602090f35b346101a95760003660031901126101a9576032546040516001600160a01b039091168152602090f35b346101a95760203660031901126101a9576004356000526033602052602060ff604060002054166040519015158152f35b346101a95760203660031901126101a9576020610e46600435610e41816101ae565b611b9f565b6001600160401b0360405191168152f35b346101a95760003660031901126101a957602047604051908152f35b346101a95760603660031901126101a957600435610e9081610198565b60243590610e9d82610198565b604435610ea981610198565b600080516020613cb083398151915254926001600160401b0360ff8560401c1615941680159081610fb9575b6001149081610faf575b159081610fa6575b50610f9457600080516020613cb0833981519152805467ffffffffffffffff19166001179055610f1b9284610f6a57611c79565b610f2157005b600080516020613cb0833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b600080516020613cb0833981519152805460ff60401b191668010000000000000000179055611c79565b60405163f92ee8a960e01b8152600490fd5b90501538610ee7565b303b159150610edf565b859150610ed5565b346101a95760003660031901126101a95760c06035546001600160801b0363ffffffff9160405192808216845260ff8260201c16602085015260ff8260281c166040850152808260301c1660608501528160501c16608084015260701c1660a0820152f35b346101a95760003660031901126101a9576060600054604051906001600160801b03811682526001600160401b038160801c16602083015260c01c6040820152f35b346101a95760203660031901126101a957600435600052603460205260606040600020600181549101546040519182526001600160801b038116602083015260801c6040820152f35b60a03660031901126101a9576004356110c981610198565b604435906110d6826101ae565b606435916110e3836101bf565b6084356001600160401b0381116101a957610021936111096103879236906004016102c3565b90835a956024359034906121f2565b346101a95760203660031901126101a95760043561113581610198565b61113d612310565b6001600160a01b03811615611155576100219061248f565b604051631e4fbdf760e01b815260006004820152602490fd5b634e487b7160e01b600052601160045260246000fd5b906001820180921161119257565b61116e565b600101908160011161119257565b9190820180921161119257565b908160209103126101a9575190565b6040513d6000823e3d90fd5b92919060ff60315460a01c166101a95760408481018051909591946001600160a01b0394909291851630146114e057603654611211906001600160a01b0316610961565b9580519363a25ae55760e01b908186528286806112368a600483019190602083019252565b03818c5afa80156103a55788966000916114c3575b50519361126961126261125d36611522565b61233c565b861461156b565b6112728661237d565b809a61128b610917836000526034602052604060002090565b6001600160801b0395866112a960208401516001600160801b031690565b1615928315611405575b505050936113da989961135061134b6020999661132261137b9761132e6113cc9e9c986112e26113be9d6115fa565b89518f810191825260006020830152908160408201039161130b601f199384810183528261026b565b5190208a5194602091869283019190602083019252565b0390810184528361026b565b61134561133961166c565b916084359436916116a0565b916123ec565b61170f565b61135861028c565b9485526001600160801b034283161688860152166001600160801b031690830152565b61138f896000526034602052604060002090565b90600190805183556001600160801b036020820151169060406001600160801b031991015160801b1617910155565b01516001600160a01b031690565b94516001600160a01b031690565b169216907f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f62600080a4565b909196949a5087935083925061142b61144e9a98889a979a01516001600160801b031690565b83519283526001600160801b031660048301529098899190829081906024820190565b03915afa9182156103a5578a9961135061134b6020998f6113da9d61132e611322926113cc9f6113be9d61137b9c6112e2926000916114a6575b505190511415999d5050989c9e5050975050969950829c9b506112b3565b6114bd91508d803d106106885761067a818361026b565b38611488565b6114da9150843d86116106885761067a818361026b565b3861124b565b85516309a4b7ed60e11b8152600490fd5b908160409103126101a95760206040519161150b836101ff565b80518352015161151a816101ae565b602082015290565b60809060431901126101a95760405190608082018281106001600160401b038211176101fa5760405281604435815260643560208201526084356040820152606060a435910152565b1561157257565b60405162461bcd60e51b815260206004820152602a60248201527f4c696768744c696e6b506f7274616c3a20696e76616c6964206f7574707574206044820152693937b7ba10383937b7b360b11b6064820152608490fd5b906040516115d78161021a565b6040600182948054845201546001600160801b038116602084015260801c910152565b1561160157565b60405162461bcd60e51b815260206004820152603860248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c206861736860448201527f2068617320616c7265616479206265656e2070726f76656e00000000000000006064820152608490fd5b60405190611679826101ff565b60018252600160f81b6020830152565b6001600160401b0381116101fa5760051b60200190565b929190926116ad84611689565b916116bb604051938461026b565b829480845260208094019060051b8301928284116101a95780915b8483106116e557505050505050565b82356001600160401b0381116101a957869161170486849386016102c3565b8152019201916116d6565b1561171657565b60405162461bcd60e51b815260206004820152603360248201527f4c696768744c696e6b506f7274616c3a20696e76616c696420776974686472616044820152723bb0b61034b731b63ab9b4b7b710383937b7b360691b6064820152608490fd5b906049916000815260006020820152610c3560c61b6040820152600060488201526117ab825180936020868501910161051a565b010190565b9290916049959492845260208401526001600160401b0360c01b9060c01b166040830152151560f81b60488201526117ab825180936020868501910161051a565b90602061180292818152019061053d565b90565b1561180c57565b60405162461bcd60e51b815260206004820152603360248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c20686173206044820152721b9bdd081899595b881c1c9bdd995b881e595d606a1b6064820152608490fd5b908160209103126101a95751611802816101ae565b1561188957565b60405162461bcd60e51b815260206004820152604c60248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c2074696d6560448201527f7374616d70206c657373207468616e204c32204f7261636c652073746172746960648201526b06e672074696d657374616d760a41b608482015260a490fd5b1561191057565b60405162461bcd60e51b815260206004820152604660248201527f4c696768744c696e6b506f7274616c3a2070726f76656e20776974686472617760448201527f616c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656064820152651b185c1cd95960d21b608482015260a490fd5b1561199157565b60405162461bcd60e51b815260206004820152604a60248201527f4c696768744c696e6b506f7274616c3a206f757470757420726f6f742070726f60448201527f76656e206973206e6f74207468652073616d652061732063757272656e74206f6064820152691d5d1c1d5d081c9bdbdd60b21b608482015260a490fd5b15611a1657565b60a460405162461bcd60e51b815260206004820152604460248201527f4c696768744c696e6b506f7274616c3a206f75747075742070726f706f73616c60448201527f2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c616064820152631c1cd95960e21b6084820152fd5b15611a9457565b60405162461bcd60e51b815260206004820152603660248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c2068617320604482015275185b1c9958591e481899595b88199a5b985b1a5e995960521b6064820152608490fd5b60001981019190821161119257565b60f61981019190821161119257565b60bf1981019190821161119257565b60b61981019190821161119257565b607f1981019190821161119257565b9190820391821161119257565b9062030d406001600160401b038093160191821161119257565b90620186a06001600160401b038093160191821161119257565b9190916001600160401b038080941691160191821161119257565b60041b680ffffffffffffffff067fffffffffffffff082169116810361119257615208016001600160401b0381116111925790565b6001600160801b0360a06102999263ffffffff8151166035549064ff00000000602084015160201b1665ff0000000000604085015160281b169069ffffffff000000000000606086015160301b169263ffffffff60501b608087015160501b16946dffffffffffffffffffffffffffff19161717171717603555015116603554906001600160801b0360701b9060701b16906001600160801b0360701b191617603555565b90611d0e929160018060a01b038092816bffffffffffffffffffffffff60a01b931683603654161760365516906037541617603755611cf7604051611cbd816101df565b6301312d008152600a602082015260086040820152633b9aca006060820152620f424060808201526001600160801b0360a0820152611bd4565b6032546001600160a01b03161615611d165761248f565b61029961259f565b603280546001600160a01b03191661dead17905561248f565b610299604051611d3e81610235565b6000815260005a913390323303611ddb575b611d9a60696040518093346020830152346040830152610c3560c51b6060830152866068830152611d8a815180926020868601910161051a565b810103604981018452018261026b565b7fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3260405180611dd3339560018060a01b031694826117f1565b0390a4611ee5565b3361111161111160901b01016001600160a01b03169150611d50565b634e487b7160e01b600052601260045260246000fd5b8015611e2057670de0b6b3a76400000590565b611df7565b8115611e2057600160ff1b8114600019831416611192570590565b8181039291600013801582851316918412161761119257565b90670de0b6b3a76400009182810292818405149015171561119257565b818102929160008212600160ff1b82141661119257818405149015171561119257565b9190916000838201938412911290801582169115161761119257565b90620186a0918083029283040361119257565b8181029291811591840414171561119257565b8115611e20570490565b600054611ef58160c01c43611b43565b90611efe612629565b91611f39611f1c611f13855163ffffffff1690565b63ffffffff1690565b611f33611f2d602087015160ff1690565b60ff1690565b90611e25565b81611fe8575b505050611f63610742611f5e6000546001600160401b039060801c1690565b611b6a565b611f77611f13600054925163ffffffff1690565b6001600160401b038260801c1613611fd657611fb0611fa16001600160801b03611fb89316611eb5565b611faa486128f7565b90611edb565b915a90611b43565b90818111611fc4575050565b61029991611fd191611b43565b612908565b6040516377ebef4d60e01b8152600490fd5b6120d0926120a9916001600160401b039361204561200b83878660801c16611e40565b61203f6120236001600160801b038097169283611e76565b611f3360408c0196612039611f2d895160ff1690565b90611e76565b90611e99565b606088019161207e61205e611f13855163ffffffff1690565b9260a08b01936120786109c686516001600160801b031690565b916126d8565b93600182116120d8575b50505050166001600160801b03166001600160801b03196000541617600055565b600080546001600160801b0316439290921660c01b6001600160c01b031916919091179055565b388080611f3f565b612114611f1361210961212297612078956121036120fd611f2d6109c6985160ff1690565b91611af8565b916126fd565b955163ffffffff1690565b92516001600160801b031690565b38808080612088565b60005461213b8160c01c43611b43565b90612144612629565b91612159611f1c611f13855163ffffffff1690565b816121c7575b5050506121846107428361217f6000546001600160401b039060801c1690565b611b84565b6000546121a0611f136001600160401b03935163ffffffff1690565b828260801c1613611fd657611fb892611fb0926001600160801b03611fa193169116611ec8565b6121ea926120a9916001600160401b039361204561200b83878660801c16611e40565b38808061215f565b9192939485806122fe575b6122ec57612218610a46610e4183516001600160401b031690565b6001600160401b038616106122da576201d4c08151116122c85760009533953233036122ac575b612288927fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3295949261227a92604051988995602087016117b0565b03601f19810185528461026b565b6040516001600160a01b03918216949091169281906122a790826117f1565b0390a4565b3361111161111160901b01016001600160a01b0316965061223f565b6040516373052b0f60e01b8152600490fd5b604051630925370160e31b8152600490fd5b6040516309a4b7ed60e11b8152600490fd5b506001600160a01b03831615156121fd565b6031546001600160a01b0316330361232457565b60405163118cdaa760e01b8152336004820152602490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261237781610250565b51902090565b80519061237760018060a01b036123de81602085015116916040850151169360608101519060a060808201519101519160405196879560208701998a5260408701526060860152608085015260a084015260c08084015260e083019061053d565b03601f19810183528261026b565b926124189192611802946020815191012060405190602082015260208152612413816101ff565b612ece565b906020815191012090602081519101201490565b6037546040516398e2dffb60e01b815290602090829060049082906001600160a01b03165afa9081156103a557600091612470575b50810180911161119257421190565b612489915060203d60201161039e57610396818361026b565b38612461565b603180546001600160a01b039283166001600160a01b0319821681179092559091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815260808101916001600160401b038311828410176101fa576102999260405261294c565b9060061b622673c001603f5a021061254c5781600092918360208194519301915af190565b6308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b929060061b622673c001603f5a021061254c576000928392602083519301915af190565b60ff600080516020613cb08339815191525460401c1615612617576000548060c01c156125c95750565b610299906001600160401b03431690604051906125e58261021a565b50633b9aca0080825260006020830181905260409290920183905260c09290921b6001600160c01b0319169091179055565b604051631afcd79f60e31b8152600490fd5b604051612635816101df565b60a06000918281528260208201528260408201528260608201528260808201520152604051612663816101df565b6118026001600160801b036035546126c263ffffffff808316865260ff8360201c16602087015260ff8360281c1660408701526126ae818460301c16606088019063ffffffff169052565b8260501c16608086019063ffffffff169052565b60701c1660a08301906001600160801b03169052565b90808213156126f55750905b808212156126f0575090565b905090565b9050906126e4565b9061270790611e0d565b90670de0b6b3a764000060008382039312818412811691841390151617611192576120396128f26128e46128e4947d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b886428461276061180298611e59565b9161276d600082136132a2565b711340daa0d5f769dba1915cef59f0815a550661278982613757565b6060928382609f03011b609f1c90605f19017d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302926c0b7a86d7375468fac667a0a527826d01920d8043ca89b5239253284e42816d0a0f742023def783a307a986912e816d13cdffb29d51d99322bdff5f2211816d0df99ac502031bf953eff472fdcc816d0388eaa27412d5aca026815d636e816c465772b2bbbb5f824b15207a308101028c1d01028a1d0102881d0102861d0102841d0102821d0191780a09507084cc699bb0e71ea869ffffffffffffffffffffffff19916cb9a025d814b29c212b8b1a07cd1990826d02384773bdf1ac5676facced609019816c8c3f38e95a6b1ff2ab1c3b343619816d02247f7a7b6594320649aa03aba1816d0139601a2efabe717e604cbb4894816c29508e458543d8aa4df2abee78810102891d0102871d0102851d0102831d0102901d0102010502010160ae1d611e76565b670de0b6b3a7640000900590565b6132da565b633b9aca00808211156126f0575090565b906000915a925b5a8403848111611192578211156129315760001981146111925760010161290f565b50509050565b908160209103126101a95751611802816101bf565b6000806129969260018060a01b03169360208151910182865af13d156129f1573d90612977826102a8565b91612985604051938461026b565b82523d6000602084013e5b836137bf565b80519081151591826129cf575b50506129ac5750565b604051635274afe760e01b81526001600160a01b03919091166004820152602490fd5b6129ea9250906020806129e6938301019101612937565b1590565b38806129a3565b606090612990565b15612a0057565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b600052603260045260246000fd5b805160011015612a635760400190565b612a3d565b805160101015612a63576102200190565b8051821015612a635760209160051b010190565b15612a9457565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b15612af757565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b15612b5257565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b15612bae57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b805115612a635760200190565b908151811015612a63570160200190565b60ff166002039060ff821161119257565b15612c2957565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15612c9b57565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15612d0d57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b15612d7f57565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15612df157565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15612e6357565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190612ee8612eee91612ee3855115156129f9565b613464565b9261351f565b90604080519081612f0760209485830160209181520190565b0391612f1b601f199384810183528261026b565b9360009283945b875186101561325057612f358689612a79565b5196612f448451871115612a8d565b856131fc57612f8e612f9391612f6f612f7b8b51868151910120895192839188830160209181520190565b0387810183528261026b565b6020815191012090602081519101201490565b612ba7565b808701968751516011811460001461304057505082518503612fe6575050505050612fe0612fcd612fc76118029451612a68565b516136fb565b93612fda85511515612dea565b51611af8565b14612e5c565b90919294939561303461302e61302860019360ff61301e61301861300a8e8c612c00565b516001600160f81b03191690565b60f81c90565b9151911690612a79565b516136b6565b97611184565b945b0194929190612f22565b94929099989795939160028096146000146131a75761306260ff999a9b613576565b9561307261301861300a89612bf3565b9661308d600191613087611f2d848c16612c11565b90613596565b9a8b986130b06130a76130a08b8a613596565b809c61364f565b809e5114612c22565b16918214801561319d575b156130f95750505050505050612fc76130f3926130e0611802966130e6945114612c94565b51612a53565b93612fda85511515612d06565b14612d78565b90809295999893969a94975015918215613193575b5050156131345760019161312861302861312e9351612a53565b986111a5565b94613036565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b149050388061310e565b50600382146130bb565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b875180518311613237575061322d61323291612f6f612f7b8b51868151910120895192839188830160209181520190565b612b4b565b612f93565b8051602091820120825191909201206132329114612af0565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b156132a957565b60405162461bcd60e51b815260206004820152600960248201526815539111519253915160ba1b6044820152606490fd5b680248ce36a70cb26b3e1981131561345e57680755bf798b4a1bf1e581121561342a576503782dace9d990604e1b0574029d9dc38563c32e5c2f6dc192ee70ef65f9978af36bb17217f7d1cf79abc9e3b398916060906001605f1b8482841b0501821d9384029003806c10fe68e7fd37d0007b713f7650810102821d906e0587f503bb6ea29d25fcb7401964506e05180bb14799ab47a8a8cb2a527d57826e02c72388d9f74f51a9331fed693f1419816db1bbb201f443cf962f1a1d3db4a5816d1a521255e34f6a5061b25ef1c9c319816d0277594991cfc85f6e2461837cd9816c240c330e9fb2d9cbaf0fd5aafb198101028d1d01028b1d0102891d0102871d0102851d01936d360d7aeea093263ecc6e0ecb291760621b936d02d16720577bd19bf614176fe9ea810190846d01d3967ed30fc4f89c02bab570811991010102901d01020105029060c3031c90565b60405162461bcd60e51b815260206004820152600c60248201526b4558505f4f564552464c4f5760a01b6044820152606490fd5b50600090565b90815161347081611689565b604061347f604051928361026b565b828252601f1961348e84611689565b019060005b8281106134fc57505050809360005b8381106134af5750505050565b806134bc60019284612a79565b516134d06134ca8386612a79565b51613822565b6134d861029b565b91825260208201526134ea8286612a79565b526134f58185612a79565b50016134a2565b602090825161350a816101ff565b60608082528382015285820183015201613493565b6040519080516001918160011b603f8101601f1916850160405284526000805b83811061354e57505050505090565b808591821b87016021600f6020808589010151871a908160041c90850153169101530161353f565b60200151805115612a635761359160206118029201516136fb565b61351f565b90815181101561363a578151818103908082116111925782906135be83601f8101101561395e565b6135d98383016135d08482101561395e565b8651101561399b565b036135f257505050604051600081526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106136275750508252601f01601f191660405290565b909283518152602080910193019061360f565b505060405161364881610235565b6000815290565b8051825160009493918591808210156136ad575094505b5b84811080613683575b1561367d57600101613667565b93505050565b506001600160f81b0319806136988386612c00565b5116906136a58385612c00565b511614613670565b90509450613666565b60208151106000146136d2576020810151905161180291613b59565b611802906136fb565b600211156136e557565b634e487b7160e01b600052602160045260246000fd5b613704816139db565b60028193929310156136e557613745578251828201908183116111925703613733576020611802930151613bd1565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6137628115156132a2565b806001600160801b031060071b81811c6001600160401b031060061b1781811c63ffffffff1060051b1781811c61ffff1060041b1781811c60ff1060031b1781811c600f1060021b1781811c60031060011b1790811c6001101790565b906137e657508051156137d457805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580613819575b6137f7575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156137ef565b61382a613c4b565b5080511561394c57602081519160405192613844846101ff565b8352602080840191018152613858836139db565b919390613864836136db565b600180930361393a5761387790856111a5565b85510361373357613886613c64565b94600094835b61389b575b5050505050815290565b81518082101561393457816138af91611b43565b818651906138bc916111a5565b6138c461029b565b918252848201526138d4906139db565b50966138e082896111a5565b838851906138ed916111a5565b6138f561029b565b91825286820152613906828b612a79565b52613911818a612a79565b5061391b90611184565b96613925916111a5565b61392e916111a5565b8361388c565b50613891565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561396557565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b156139a257565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b9081511561394c576020820151805160001a607f8111613a02575060009250600191839150565b60b78111613a7657613a1390611b34565b8093511115613a6457600190810151908314906001600160f81b03191681613a56575b50613a445760019190600090565b60405163babb01dd60e01b8152600490fd5b600160ff1b11905038613a36565b6040516366c9448560e01b8152600490fd5b60bf8111613ada57613a8790611b25565b908184511115613a645760010180516001600160f81b03191615613a4457518160031b610100031c926037841115613a445751613ac484836111a5565b1015613a6457613ad390611197565b9190600090565b60f78111613afd57613aec9150611b16565b8092511115613a6457600191908290565b613b0690611b07565b908184511115613a645760010180516001600160f81b03191615613a4457518160031b610100031c926037841115613a445751613b4384836111a5565b1015613a6457613b5290611197565b9190600190565b9190613b64816102a8565b92613b72604051948561026b565b818452613b7e826102a8565b602090601f19013686830137848315613bca575082916000915b838310613bb457505011613ba95750565b602060009184010152565b8183015187840182015284935091820191613b98565b9450505050565b92919092613bde826102a8565b93613bec604051958661026b565b828552613bf8836102a8565b60209190601f19013687840137858415613c43575082018092116111925782916000915b838310613c2d57505011613ba95750565b8183015187840182015284935091820191613c1c565b955050505050565b60405190613c58826101ff565b60006020838281520152565b6040519061042082018281106001600160401b038211176101fa5760405260208083528260005b6104008110613c9957505050565b8290613ca3613c4b565b82828501015201613c8b56fef0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212203821fa16302ede462a84de5cff8ab41ea75c7178136e741508c77bd4d9a9df0664736f6c63430008160033
Deployed Bytecode
0x60806040526004361015610023575b361561001957600080fd5b610021611d2f565b005b60003560e01c8063149f2f22146101935780633f4ba83a1461018e5780634870496f1461018957806354fd4d50146101845780635c975abb1461017f5780636dbffb781461017a578063715018a61461017557806371cfaa3f146101705780638456cb591461016b5780638b4c40b0146101665780638c3152e9146101615780638da5cb5b1461015c5780639b5f694a146101575780639bf62d8214610152578063a14238e71461014d578063a35d99df14610148578063b69ef8a814610143578063c0c53b8b1461013e578063cc731b0214610139578063cff0ab9614610134578063e965084c1461012f578063e9e05c421461012a5763f2fde38b0361000e57611118565b6110b1565b611068565b611026565b610fc1565b610e73565b610e57565b610e1f565b610dee565b610dc5565b610d9c565b610d73565b61089d565b61088f565b610830565b6106ed565b61068f565b6105d9565b6105b3565b610562565b61049a565b6103ba565b61030a565b6001600160a01b038116036101a957565b600080fd5b6001600160401b038116036101a957565b801515036101a957565b634e487b7160e01b600052604160045260246000fd5b60c081019081106001600160401b038211176101fa57604052565b6101c9565b604081019081106001600160401b038211176101fa57604052565b606081019081106001600160401b038211176101fa57604052565b602081019081106001600160401b038211176101fa57604052565b60a081019081106001600160401b038211176101fa57604052565b90601f801991011681019081106001600160401b038211176101fa57604052565b604051906102998261021a565b565b60405190610299826101ff565b6001600160401b0381116101fa57601f01601f191660200190565b81601f820112156101a9578035906102da826102a8565b926102e8604051948561026b565b828452602083830101116101a957816000926020809301838601378301015290565b346101a95760c03660031901126101a957600480359061032982610198565b602435916064359061033a826101ae565b60843590610347826101bf565b60a4356001600160401b0381116101a95761036590369086016102c3565b945a9473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6103aa565b603855565b61212b565b610396818361026b565b8101906111b2565b503d61038c565b6111c1565b50604051630eaf3c0f60e01b8152fd5b346101a957600080600319360112610418576103d4612310565b60315460ff8160a01c16156104145760ff60a01b19166031557f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b338180a180f35b5080fd5b80fd5b919060c0838203126101a95760405190610434826101df565b819380358352602081013561044881610198565b6020840152604081013561045b81610198565b6040840152606081013560608401526080810135608084015260a0810135916001600160401b0383116101a95760a09261049592016102c3565b910152565b346101a95760e03660031901126101a9576001600160401b036004358181116101a9576104cb90369060040161041b565b9060803660431901126101a95760c435918183116101a957366023840112156101a95782600401359182116101a9573660248360051b850101116101a9576024610021930190602435906111cd565b60005b83811061052d5750506000910152565b818101518382015260200161051d565b906020916105568151809281855285808601910161051a565b601f01601f1916010190565b346101a95760003660031901126101a9576105af604051610582816101ff565b600c81526b322e382e312d626574612e3160a01b602082015260405191829160208352602083019061053d565b0390f35b346101a95760003660031901126101a957602060ff60315460a01c166040519015158152f35b346101a95760203660031901126101a9576036546040805163a25ae55760e01b8152600480359082015291829060249082906001600160a01b03165afa80156103a5576106496001600160401b03602061064e936105af95600091610660575b500151166001600160401b031690565b61242c565b60405190151581529081906020820190565b610682915060403d604011610688575b61067a818361026b565b8101906114f1565b38610639565b503d610670565b346101a957600080600319360112610418576106a9612310565b603180546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b346101a95760803660031901126101a95760043561070a81610198565b60243560ff811681036101a9576107df6107d391600093610729612310565b61076f6107426001600160401b03875460801c16611b50565b6000805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b6040516371cfaa3f60e01b60208201526001600160a01b0391909116602482015260ff9190911660448083019190915235606480830191909152356084808301919091528152601f19906107c460a48261026b565b60405193849160208301611777565b0390810183528261026b565b73deaddeaddeaddeaddeaddeaddeaddeaddead00017fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c326040518061082b6015602160991b0195826117f1565b0390a4005b346101a9576000806003193601126104185761084a612310565b60315460ff8160a01c166104145760ff60a01b1916600160a01b176031557f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff6258180a180f35b60003660031901126101a957005b346101a9576020806003193601126101a9576004906001600160401b039082358281116101a9576108d1903690850161041b565b60ff60315460a01c166101a9576032546001600160a01b039190821661deac1901610d62576108ff8161237d565b9261091c610917856000526034602052604060002090565b6115ca565b818101876001600160801b036109458161093d85516001600160801b031690565b161515611805565b82516001600160801b031660365461096d906001600160a01b03165b6001600160a01b031690565b9160409a878c51809663443c313960e11b825281875afa80156103a557610a07966109b96109d7958f9895610649956109c6956109d2988f600092610d35575b50501691161015611882565b516001600160801b031690565b6001600160801b031690565b611909565b81840151825163a25ae55760e01b81526001600160801b039091168b820190815290938492918391829160200190565b03915afa80156103a557610a468461064992610a5795610a5295600092610d12575b50610a3890825190511461198a565b01516001600160401b031690565b6001600160401b031690565b611a0f565b610a7e610a78610a71866000526033602052604060002090565b5460ff1690565b15611a8d565b610aa2610a95856000526033602052604060002090565b805460ff19166001179055565b80820151603280546001600160a01b0319166001600160a01b0390921691909117905560009273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8403610b8b57505080840151610b2492506001600160a01b031690608081015160a060608301519201519261257b565b905b603280546001600160a01b03191661dead179055825182151581527fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b90602090a21580610b81575b610b7457005b5163eeae4ed360e01b8152fd5b5060013214610b6e565b8286018051909488918190610ba8906001600160a01b0316610961565b14610d03576060850193845180610bfc575b50505050505060a08101519081511515600014610bf1579151608090920151610beb926001600160a01b0316612527565b90610b26565b505050600190610b26565b610382610c0b91603854611b43565b88516370a0823160e01b80825230948201948552938290829081906020010381865afa9283156103a5578b918694610cdc575b50885187519495610c7a9585949392610c6191906001600160a01b0316846124d8565b8c51908152309281019283529485928391829160200190565b03915afa9384156103a55793610cb9575b5050610c9991925190611b43565b03610ca957388086818080610bba565b505050516312171d8360e31b8152fd5b610c9993509081610cd592903d1061039e57610396818361026b565b9138610c8b565b610c7a945090610cfa849392843d861161039e57610396818361026b565b94509091610c3e565b5086516309a4b7ed60e11b8152fd5b610a38919250610d2e908d803d106106885761067a818361026b565b9190610a29565b610d549250803d10610d5b575b610d4c818361026b565b81019061186d565b388f6109ad565b503d610d42565b6040516349cb68ab60e11b81528590fd5b346101a95760003660031901126101a9576031546040516001600160a01b039091168152602090f35b346101a95760003660031901126101a9576036546040516001600160a01b039091168152602090f35b346101a95760003660031901126101a9576032546040516001600160a01b039091168152602090f35b346101a95760203660031901126101a9576004356000526033602052602060ff604060002054166040519015158152f35b346101a95760203660031901126101a9576020610e46600435610e41816101ae565b611b9f565b6001600160401b0360405191168152f35b346101a95760003660031901126101a957602047604051908152f35b346101a95760603660031901126101a957600435610e9081610198565b60243590610e9d82610198565b604435610ea981610198565b600080516020613cb083398151915254926001600160401b0360ff8560401c1615941680159081610fb9575b6001149081610faf575b159081610fa6575b50610f9457600080516020613cb0833981519152805467ffffffffffffffff19166001179055610f1b9284610f6a57611c79565b610f2157005b600080516020613cb0833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b600080516020613cb0833981519152805460ff60401b191668010000000000000000179055611c79565b60405163f92ee8a960e01b8152600490fd5b90501538610ee7565b303b159150610edf565b859150610ed5565b346101a95760003660031901126101a95760c06035546001600160801b0363ffffffff9160405192808216845260ff8260201c16602085015260ff8260281c166040850152808260301c1660608501528160501c16608084015260701c1660a0820152f35b346101a95760003660031901126101a9576060600054604051906001600160801b03811682526001600160401b038160801c16602083015260c01c6040820152f35b346101a95760203660031901126101a957600435600052603460205260606040600020600181549101546040519182526001600160801b038116602083015260801c6040820152f35b60a03660031901126101a9576004356110c981610198565b604435906110d6826101ae565b606435916110e3836101bf565b6084356001600160401b0381116101a957610021936111096103879236906004016102c3565b90835a956024359034906121f2565b346101a95760203660031901126101a95760043561113581610198565b61113d612310565b6001600160a01b03811615611155576100219061248f565b604051631e4fbdf760e01b815260006004820152602490fd5b634e487b7160e01b600052601160045260246000fd5b906001820180921161119257565b61116e565b600101908160011161119257565b9190820180921161119257565b908160209103126101a9575190565b6040513d6000823e3d90fd5b92919060ff60315460a01c166101a95760408481018051909591946001600160a01b0394909291851630146114e057603654611211906001600160a01b0316610961565b9580519363a25ae55760e01b908186528286806112368a600483019190602083019252565b03818c5afa80156103a55788966000916114c3575b50519361126961126261125d36611522565b61233c565b861461156b565b6112728661237d565b809a61128b610917836000526034602052604060002090565b6001600160801b0395866112a960208401516001600160801b031690565b1615928315611405575b505050936113da989961135061134b6020999661132261137b9761132e6113cc9e9c986112e26113be9d6115fa565b89518f810191825260006020830152908160408201039161130b601f199384810183528261026b565b5190208a5194602091869283019190602083019252565b0390810184528361026b565b61134561133961166c565b916084359436916116a0565b916123ec565b61170f565b61135861028c565b9485526001600160801b034283161688860152166001600160801b031690830152565b61138f896000526034602052604060002090565b90600190805183556001600160801b036020820151169060406001600160801b031991015160801b1617910155565b01516001600160a01b031690565b94516001600160a01b031690565b169216907f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f62600080a4565b909196949a5087935083925061142b61144e9a98889a979a01516001600160801b031690565b83519283526001600160801b031660048301529098899190829081906024820190565b03915afa9182156103a5578a9961135061134b6020998f6113da9d61132e611322926113cc9f6113be9d61137b9c6112e2926000916114a6575b505190511415999d5050989c9e5050975050969950829c9b506112b3565b6114bd91508d803d106106885761067a818361026b565b38611488565b6114da9150843d86116106885761067a818361026b565b3861124b565b85516309a4b7ed60e11b8152600490fd5b908160409103126101a95760206040519161150b836101ff565b80518352015161151a816101ae565b602082015290565b60809060431901126101a95760405190608082018281106001600160401b038211176101fa5760405281604435815260643560208201526084356040820152606060a435910152565b1561157257565b60405162461bcd60e51b815260206004820152602a60248201527f4c696768744c696e6b506f7274616c3a20696e76616c6964206f7574707574206044820152693937b7ba10383937b7b360b11b6064820152608490fd5b906040516115d78161021a565b6040600182948054845201546001600160801b038116602084015260801c910152565b1561160157565b60405162461bcd60e51b815260206004820152603860248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c206861736860448201527f2068617320616c7265616479206265656e2070726f76656e00000000000000006064820152608490fd5b60405190611679826101ff565b60018252600160f81b6020830152565b6001600160401b0381116101fa5760051b60200190565b929190926116ad84611689565b916116bb604051938461026b565b829480845260208094019060051b8301928284116101a95780915b8483106116e557505050505050565b82356001600160401b0381116101a957869161170486849386016102c3565b8152019201916116d6565b1561171657565b60405162461bcd60e51b815260206004820152603360248201527f4c696768744c696e6b506f7274616c3a20696e76616c696420776974686472616044820152723bb0b61034b731b63ab9b4b7b710383937b7b360691b6064820152608490fd5b906049916000815260006020820152610c3560c61b6040820152600060488201526117ab825180936020868501910161051a565b010190565b9290916049959492845260208401526001600160401b0360c01b9060c01b166040830152151560f81b60488201526117ab825180936020868501910161051a565b90602061180292818152019061053d565b90565b1561180c57565b60405162461bcd60e51b815260206004820152603360248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c20686173206044820152721b9bdd081899595b881c1c9bdd995b881e595d606a1b6064820152608490fd5b908160209103126101a95751611802816101ae565b1561188957565b60405162461bcd60e51b815260206004820152604c60248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c2074696d6560448201527f7374616d70206c657373207468616e204c32204f7261636c652073746172746960648201526b06e672074696d657374616d760a41b608482015260a490fd5b1561191057565b60405162461bcd60e51b815260206004820152604660248201527f4c696768744c696e6b506f7274616c3a2070726f76656e20776974686472617760448201527f616c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656064820152651b185c1cd95960d21b608482015260a490fd5b1561199157565b60405162461bcd60e51b815260206004820152604a60248201527f4c696768744c696e6b506f7274616c3a206f757470757420726f6f742070726f60448201527f76656e206973206e6f74207468652073616d652061732063757272656e74206f6064820152691d5d1c1d5d081c9bdbdd60b21b608482015260a490fd5b15611a1657565b60a460405162461bcd60e51b815260206004820152604460248201527f4c696768744c696e6b506f7274616c3a206f75747075742070726f706f73616c60448201527f2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c616064820152631c1cd95960e21b6084820152fd5b15611a9457565b60405162461bcd60e51b815260206004820152603660248201527f4c696768744c696e6b506f7274616c3a207769746864726177616c2068617320604482015275185b1c9958591e481899595b88199a5b985b1a5e995960521b6064820152608490fd5b60001981019190821161119257565b60f61981019190821161119257565b60bf1981019190821161119257565b60b61981019190821161119257565b607f1981019190821161119257565b9190820391821161119257565b9062030d406001600160401b038093160191821161119257565b90620186a06001600160401b038093160191821161119257565b9190916001600160401b038080941691160191821161119257565b60041b680ffffffffffffffff067fffffffffffffff082169116810361119257615208016001600160401b0381116111925790565b6001600160801b0360a06102999263ffffffff8151166035549064ff00000000602084015160201b1665ff0000000000604085015160281b169069ffffffff000000000000606086015160301b169263ffffffff60501b608087015160501b16946dffffffffffffffffffffffffffff19161717171717603555015116603554906001600160801b0360701b9060701b16906001600160801b0360701b191617603555565b90611d0e929160018060a01b038092816bffffffffffffffffffffffff60a01b931683603654161760365516906037541617603755611cf7604051611cbd816101df565b6301312d008152600a602082015260086040820152633b9aca006060820152620f424060808201526001600160801b0360a0820152611bd4565b6032546001600160a01b03161615611d165761248f565b61029961259f565b603280546001600160a01b03191661dead17905561248f565b610299604051611d3e81610235565b6000815260005a913390323303611ddb575b611d9a60696040518093346020830152346040830152610c3560c51b6060830152866068830152611d8a815180926020868601910161051a565b810103604981018452018261026b565b7fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3260405180611dd3339560018060a01b031694826117f1565b0390a4611ee5565b3361111161111160901b01016001600160a01b03169150611d50565b634e487b7160e01b600052601260045260246000fd5b8015611e2057670de0b6b3a76400000590565b611df7565b8115611e2057600160ff1b8114600019831416611192570590565b8181039291600013801582851316918412161761119257565b90670de0b6b3a76400009182810292818405149015171561119257565b818102929160008212600160ff1b82141661119257818405149015171561119257565b9190916000838201938412911290801582169115161761119257565b90620186a0918083029283040361119257565b8181029291811591840414171561119257565b8115611e20570490565b600054611ef58160c01c43611b43565b90611efe612629565b91611f39611f1c611f13855163ffffffff1690565b63ffffffff1690565b611f33611f2d602087015160ff1690565b60ff1690565b90611e25565b81611fe8575b505050611f63610742611f5e6000546001600160401b039060801c1690565b611b6a565b611f77611f13600054925163ffffffff1690565b6001600160401b038260801c1613611fd657611fb0611fa16001600160801b03611fb89316611eb5565b611faa486128f7565b90611edb565b915a90611b43565b90818111611fc4575050565b61029991611fd191611b43565b612908565b6040516377ebef4d60e01b8152600490fd5b6120d0926120a9916001600160401b039361204561200b83878660801c16611e40565b61203f6120236001600160801b038097169283611e76565b611f3360408c0196612039611f2d895160ff1690565b90611e76565b90611e99565b606088019161207e61205e611f13855163ffffffff1690565b9260a08b01936120786109c686516001600160801b031690565b916126d8565b93600182116120d8575b50505050166001600160801b03166001600160801b03196000541617600055565b600080546001600160801b0316439290921660c01b6001600160c01b031916919091179055565b388080611f3f565b612114611f1361210961212297612078956121036120fd611f2d6109c6985160ff1690565b91611af8565b916126fd565b955163ffffffff1690565b92516001600160801b031690565b38808080612088565b60005461213b8160c01c43611b43565b90612144612629565b91612159611f1c611f13855163ffffffff1690565b816121c7575b5050506121846107428361217f6000546001600160401b039060801c1690565b611b84565b6000546121a0611f136001600160401b03935163ffffffff1690565b828260801c1613611fd657611fb892611fb0926001600160801b03611fa193169116611ec8565b6121ea926120a9916001600160401b039361204561200b83878660801c16611e40565b38808061215f565b9192939485806122fe575b6122ec57612218610a46610e4183516001600160401b031690565b6001600160401b038616106122da576201d4c08151116122c85760009533953233036122ac575b612288927fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3295949261227a92604051988995602087016117b0565b03601f19810185528461026b565b6040516001600160a01b03918216949091169281906122a790826117f1565b0390a4565b3361111161111160901b01016001600160a01b0316965061223f565b6040516373052b0f60e01b8152600490fd5b604051630925370160e31b8152600490fd5b6040516309a4b7ed60e11b8152600490fd5b506001600160a01b03831615156121fd565b6031546001600160a01b0316330361232457565b60405163118cdaa760e01b8152336004820152602490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261237781610250565b51902090565b80519061237760018060a01b036123de81602085015116916040850151169360608101519060a060808201519101519160405196879560208701998a5260408701526060860152608085015260a084015260c08084015260e083019061053d565b03601f19810183528261026b565b926124189192611802946020815191012060405190602082015260208152612413816101ff565b612ece565b906020815191012090602081519101201490565b6037546040516398e2dffb60e01b815290602090829060049082906001600160a01b03165afa9081156103a557600091612470575b50810180911161119257421190565b612489915060203d60201161039e57610396818361026b565b38612461565b603180546001600160a01b039283166001600160a01b0319821681179092559091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815260808101916001600160401b038311828410176101fa576102999260405261294c565b9060061b622673c001603f5a021061254c5781600092918360208194519301915af190565b6308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b929060061b622673c001603f5a021061254c576000928392602083519301915af190565b60ff600080516020613cb08339815191525460401c1615612617576000548060c01c156125c95750565b610299906001600160401b03431690604051906125e58261021a565b50633b9aca0080825260006020830181905260409290920183905260c09290921b6001600160c01b0319169091179055565b604051631afcd79f60e31b8152600490fd5b604051612635816101df565b60a06000918281528260208201528260408201528260608201528260808201520152604051612663816101df565b6118026001600160801b036035546126c263ffffffff808316865260ff8360201c16602087015260ff8360281c1660408701526126ae818460301c16606088019063ffffffff169052565b8260501c16608086019063ffffffff169052565b60701c1660a08301906001600160801b03169052565b90808213156126f55750905b808212156126f0575090565b905090565b9050906126e4565b9061270790611e0d565b90670de0b6b3a764000060008382039312818412811691841390151617611192576120396128f26128e46128e4947d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b886428461276061180298611e59565b9161276d600082136132a2565b711340daa0d5f769dba1915cef59f0815a550661278982613757565b6060928382609f03011b609f1c90605f19017d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302926c0b7a86d7375468fac667a0a527826d01920d8043ca89b5239253284e42816d0a0f742023def783a307a986912e816d13cdffb29d51d99322bdff5f2211816d0df99ac502031bf953eff472fdcc816d0388eaa27412d5aca026815d636e816c465772b2bbbb5f824b15207a308101028c1d01028a1d0102881d0102861d0102841d0102821d0191780a09507084cc699bb0e71ea869ffffffffffffffffffffffff19916cb9a025d814b29c212b8b1a07cd1990826d02384773bdf1ac5676facced609019816c8c3f38e95a6b1ff2ab1c3b343619816d02247f7a7b6594320649aa03aba1816d0139601a2efabe717e604cbb4894816c29508e458543d8aa4df2abee78810102891d0102871d0102851d0102831d0102901d0102010502010160ae1d611e76565b670de0b6b3a7640000900590565b6132da565b633b9aca00808211156126f0575090565b906000915a925b5a8403848111611192578211156129315760001981146111925760010161290f565b50509050565b908160209103126101a95751611802816101bf565b6000806129969260018060a01b03169360208151910182865af13d156129f1573d90612977826102a8565b91612985604051938461026b565b82523d6000602084013e5b836137bf565b80519081151591826129cf575b50506129ac5750565b604051635274afe760e01b81526001600160a01b03919091166004820152602490fd5b6129ea9250906020806129e6938301019101612937565b1590565b38806129a3565b606090612990565b15612a0057565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b600052603260045260246000fd5b805160011015612a635760400190565b612a3d565b805160101015612a63576102200190565b8051821015612a635760209160051b010190565b15612a9457565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b15612af757565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b15612b5257565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b15612bae57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b805115612a635760200190565b908151811015612a63570160200190565b60ff166002039060ff821161119257565b15612c2957565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15612c9b57565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15612d0d57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b15612d7f57565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15612df157565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15612e6357565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190612ee8612eee91612ee3855115156129f9565b613464565b9261351f565b90604080519081612f0760209485830160209181520190565b0391612f1b601f199384810183528261026b565b9360009283945b875186101561325057612f358689612a79565b5196612f448451871115612a8d565b856131fc57612f8e612f9391612f6f612f7b8b51868151910120895192839188830160209181520190565b0387810183528261026b565b6020815191012090602081519101201490565b612ba7565b808701968751516011811460001461304057505082518503612fe6575050505050612fe0612fcd612fc76118029451612a68565b516136fb565b93612fda85511515612dea565b51611af8565b14612e5c565b90919294939561303461302e61302860019360ff61301e61301861300a8e8c612c00565b516001600160f81b03191690565b60f81c90565b9151911690612a79565b516136b6565b97611184565b945b0194929190612f22565b94929099989795939160028096146000146131a75761306260ff999a9b613576565b9561307261301861300a89612bf3565b9661308d600191613087611f2d848c16612c11565b90613596565b9a8b986130b06130a76130a08b8a613596565b809c61364f565b809e5114612c22565b16918214801561319d575b156130f95750505050505050612fc76130f3926130e0611802966130e6945114612c94565b51612a53565b93612fda85511515612d06565b14612d78565b90809295999893969a94975015918215613193575b5050156131345760019161312861302861312e9351612a53565b986111a5565b94613036565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b149050388061310e565b50600382146130bb565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b875180518311613237575061322d61323291612f6f612f7b8b51868151910120895192839188830160209181520190565b612b4b565b612f93565b8051602091820120825191909201206132329114612af0565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b156132a957565b60405162461bcd60e51b815260206004820152600960248201526815539111519253915160ba1b6044820152606490fd5b680248ce36a70cb26b3e1981131561345e57680755bf798b4a1bf1e581121561342a576503782dace9d990604e1b0574029d9dc38563c32e5c2f6dc192ee70ef65f9978af36bb17217f7d1cf79abc9e3b398916060906001605f1b8482841b0501821d9384029003806c10fe68e7fd37d0007b713f7650810102821d906e0587f503bb6ea29d25fcb7401964506e05180bb14799ab47a8a8cb2a527d57826e02c72388d9f74f51a9331fed693f1419816db1bbb201f443cf962f1a1d3db4a5816d1a521255e34f6a5061b25ef1c9c319816d0277594991cfc85f6e2461837cd9816c240c330e9fb2d9cbaf0fd5aafb198101028d1d01028b1d0102891d0102871d0102851d01936d360d7aeea093263ecc6e0ecb291760621b936d02d16720577bd19bf614176fe9ea810190846d01d3967ed30fc4f89c02bab570811991010102901d01020105029060c3031c90565b60405162461bcd60e51b815260206004820152600c60248201526b4558505f4f564552464c4f5760a01b6044820152606490fd5b50600090565b90815161347081611689565b604061347f604051928361026b565b828252601f1961348e84611689565b019060005b8281106134fc57505050809360005b8381106134af5750505050565b806134bc60019284612a79565b516134d06134ca8386612a79565b51613822565b6134d861029b565b91825260208201526134ea8286612a79565b526134f58185612a79565b50016134a2565b602090825161350a816101ff565b60608082528382015285820183015201613493565b6040519080516001918160011b603f8101601f1916850160405284526000805b83811061354e57505050505090565b808591821b87016021600f6020808589010151871a908160041c90850153169101530161353f565b60200151805115612a635761359160206118029201516136fb565b61351f565b90815181101561363a578151818103908082116111925782906135be83601f8101101561395e565b6135d98383016135d08482101561395e565b8651101561399b565b036135f257505050604051600081526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106136275750508252601f01601f191660405290565b909283518152602080910193019061360f565b505060405161364881610235565b6000815290565b8051825160009493918591808210156136ad575094505b5b84811080613683575b1561367d57600101613667565b93505050565b506001600160f81b0319806136988386612c00565b5116906136a58385612c00565b511614613670565b90509450613666565b60208151106000146136d2576020810151905161180291613b59565b611802906136fb565b600211156136e557565b634e487b7160e01b600052602160045260246000fd5b613704816139db565b60028193929310156136e557613745578251828201908183116111925703613733576020611802930151613bd1565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6137628115156132a2565b806001600160801b031060071b81811c6001600160401b031060061b1781811c63ffffffff1060051b1781811c61ffff1060041b1781811c60ff1060031b1781811c600f1060021b1781811c60031060011b1790811c6001101790565b906137e657508051156137d457805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580613819575b6137f7575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156137ef565b61382a613c4b565b5080511561394c57602081519160405192613844846101ff565b8352602080840191018152613858836139db565b919390613864836136db565b600180930361393a5761387790856111a5565b85510361373357613886613c64565b94600094835b61389b575b5050505050815290565b81518082101561393457816138af91611b43565b818651906138bc916111a5565b6138c461029b565b918252848201526138d4906139db565b50966138e082896111a5565b838851906138ed916111a5565b6138f561029b565b91825286820152613906828b612a79565b52613911818a612a79565b5061391b90611184565b96613925916111a5565b61392e916111a5565b8361388c565b50613891565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561396557565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b156139a257565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b9081511561394c576020820151805160001a607f8111613a02575060009250600191839150565b60b78111613a7657613a1390611b34565b8093511115613a6457600190810151908314906001600160f81b03191681613a56575b50613a445760019190600090565b60405163babb01dd60e01b8152600490fd5b600160ff1b11905038613a36565b6040516366c9448560e01b8152600490fd5b60bf8111613ada57613a8790611b25565b908184511115613a645760010180516001600160f81b03191615613a4457518160031b610100031c926037841115613a445751613ac484836111a5565b1015613a6457613ad390611197565b9190600090565b60f78111613afd57613aec9150611b16565b8092511115613a6457600191908290565b613b0690611b07565b908184511115613a645760010180516001600160f81b03191615613a4457518160031b610100031c926037841115613a445751613b4384836111a5565b1015613a6457613b5290611197565b9190600190565b9190613b64816102a8565b92613b72604051948561026b565b818452613b7e826102a8565b602090601f19013686830137848315613bca575082916000915b838310613bb457505011613ba95750565b602060009184010152565b8183015187840182015284935091820191613b98565b9450505050565b92919092613bde826102a8565b93613bec604051958661026b565b828552613bf8836102a8565b60209190601f19013687840137858415613c43575082018092116111925782916000915b838310613c2d57505011613ba95750565b8183015187840182015284935091820191613c1c565b955050505050565b60405190613c58826101ff565b60006020838281520152565b6040519061042082018281106001600160401b038211176101fa5760405260208083528260005b6104008110613c9957505050565b8290613ca3613c4b565b82828501015201613c8b56fef0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212203821fa16302ede462a84de5cff8ab41ea75c7178136e741508c77bd4d9a9df0664736f6c63430008160033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.