Source Code
Overview
ETH Balance
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
L2ScrollMessenger
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {IL2ScrollMessenger} from "./IL2ScrollMessenger.sol";
import {L2MessageQueue} from "./predeploys/L2MessageQueue.sol";
import {PatriciaMerkleTrieVerifier} from "../libraries/verifier/PatriciaMerkleTrieVerifier.sol";
import {ScrollConstants} from "../libraries/constants/ScrollConstants.sol";
import {AddressAliasHelper} from "../libraries/common/AddressAliasHelper.sol";
import {IScrollMessenger} from "../libraries/IScrollMessenger.sol";
import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol";
// solhint-disable reason-string
// solhint-disable not-rely-on-time
/// @title L2ScrollMessenger
/// @notice The `L2ScrollMessenger` contract can:
///
/// 1. send messages from layer 2 to layer 1;
/// 2. relay messages from layer 1 layer 2;
/// 3. drop expired message due to sequencer problems.
///
/// @dev It should be a predeployed contract on layer 2 and should hold infinite amount
/// of Ether (Specifically, `uint256(-1)`), which can be initialized in Genesis Block.
contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
/*************
* Constants *
*************/
/// @notice The address of L2MessageQueue.
address public immutable messageQueue;
/*************
* Variables *
*************/
/// @notice Mapping from L2 message hash to the timestamp when the message is sent.
mapping(bytes32 => uint256) public messageSendTimestamp;
/// @notice Mapping from L1 message hash to a boolean value indicating if the message has been successfully executed.
mapping(bytes32 => bool) public isL1MessageExecuted;
/// @dev The storage slots used by previous versions of this contract.
uint256[2] private __used;
/***************
* Constructor *
***************/
constructor(address _counterpart, address _messageQueue) ScrollMessengerBase(_counterpart) {
if (_messageQueue == address(0)) {
revert ErrorZeroAddress();
}
_disableInitializers();
messageQueue = _messageQueue;
}
function initialize(address) external initializer {
ScrollMessengerBase.__ScrollMessengerBase_init(address(0), address(0));
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IScrollMessenger
function sendMessage(
address _to,
uint256 _value,
bytes memory _message,
uint256 _gasLimit
) external payable override whenNotPaused {
_sendMessage(_to, _value, _message, _gasLimit);
}
/// @inheritdoc IScrollMessenger
function sendMessage(
address _to,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit,
address
) external payable override whenNotPaused {
_sendMessage(_to, _value, _message, _gasLimit);
}
/// @inheritdoc IL2ScrollMessenger
function relayMessage(
address _from,
address _to,
uint256 _value,
uint256 _nonce,
bytes memory _message
) external override whenNotPaused {
// It is impossible to deploy a contract with the same address, reentrance is prevented in nature.
require(AddressAliasHelper.undoL1ToL2Alias(_msgSender()) == counterpart, "Caller is not L1ScrollMessenger");
bytes32 _xDomainCalldataHash = keccak256(_encodeXDomainCalldata(_from, _to, _value, _nonce, _message));
require(!isL1MessageExecuted[_xDomainCalldataHash], "Message was already successfully executed");
_executeMessage(_from, _to, _value, _message, _xDomainCalldataHash);
}
/**********************
* Internal Functions *
**********************/
/// @dev Internal function to send cross domain message.
/// @param _to The address of account who receive the message.
/// @param _value The amount of ether passed when call target contract.
/// @param _message The content of the message.
/// @param _gasLimit Optional gas limit to complete the message relay on corresponding chain.
function _sendMessage(
address _to,
uint256 _value,
bytes memory _message,
uint256 _gasLimit
) internal nonReentrant {
require(msg.value == _value, "msg.value mismatch");
uint256 _nonce = L2MessageQueue(messageQueue).nextMessageIndex();
bytes32 _xDomainCalldataHash = keccak256(_encodeXDomainCalldata(_msgSender(), _to, _value, _nonce, _message));
// normally this won't happen, since each message has different nonce, but just in case.
require(messageSendTimestamp[_xDomainCalldataHash] == 0, "Duplicated message");
messageSendTimestamp[_xDomainCalldataHash] = block.timestamp;
L2MessageQueue(messageQueue).appendMessage(_xDomainCalldataHash);
emit SentMessage(_msgSender(), _to, _value, _nonce, _gasLimit, _message);
}
/// @dev Internal function to execute a L1 => L2 message.
/// @param _from The address of the sender of the message.
/// @param _to The address of the recipient of the message.
/// @param _value The msg.value passed to the message call.
/// @param _message The content of the message.
/// @param _xDomainCalldataHash The hash of the message.
function _executeMessage(
address _from,
address _to,
uint256 _value,
bytes memory _message,
bytes32 _xDomainCalldataHash
) internal {
// @note check more `_to` address to avoid attack in the future when we add more gateways.
require(_to != messageQueue, "Forbid to call message queue");
_validateTargetAddress(_to);
// @note This usually will never happen, just in case.
require(_from != xDomainMessageSender, "Invalid message sender");
xDomainMessageSender = _from;
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = _to.call{value: _value}(_message);
// reset value to refund gas.
xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER;
if (success) {
isL1MessageExecuted[_xDomainCalldataHash] = true;
emit RelayedMessage(_xDomainCalldataHash);
} else {
emit FailedRelayedMessage(_xDomainCalldataHash);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. 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 {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```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 Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* 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 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import {IScrollMessenger} from "../libraries/IScrollMessenger.sol";
interface IL2ScrollMessenger is IScrollMessenger {
/**********
* Events *
**********/
/// @notice Emitted when the maximum number of times each message can fail in L2 is updated.
/// @param oldMaxFailedExecutionTimes The old maximum number of times each message can fail in L2.
/// @param newMaxFailedExecutionTimes The new maximum number of times each message can fail in L2.
event UpdateMaxFailedExecutionTimes(uint256 oldMaxFailedExecutionTimes, uint256 newMaxFailedExecutionTimes);
/*****************************
* Public Mutating Functions *
*****************************/
/// @notice execute L1 => L2 message
/// @dev Make sure this is only called by privileged accounts.
/// @param from The address of the sender of the message.
/// @param to The address of the recipient of the message.
/// @param value The msg.value passed to the message call.
/// @param nonce The nonce of the message to avoid replay attack.
/// @param message The content of the message.
function relayMessage(
address from,
address to,
uint256 value,
uint256 nonce,
bytes calldata message
) external;
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {AppendOnlyMerkleTree} from "../../libraries/common/AppendOnlyMerkleTree.sol";
import {OwnableBase} from "../../libraries/common/OwnableBase.sol";
/// @title L2MessageQueue
/// @notice The original idea is from Optimism, see [OVM_L2ToL1MessagePasser](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol).
/// The L2 to L1 Message Passer is a utility contract which facilitate an L1 proof of the
/// of a message on L2. The L1 Cross Domain Messenger performs this proof in its
/// _verifyStorageProof function, which verifies the existence of the transaction hash in this
/// contract's `sentMessages` mapping.
contract L2MessageQueue is AppendOnlyMerkleTree, OwnableBase {
/**********
* Events *
**********/
/// @notice Emitted when a new message is added to the merkle tree.
/// @param index The index of the corresponding message.
/// @param messageHash The hash of the corresponding message.
event AppendMessage(uint256 index, bytes32 messageHash);
/*************
* Variables *
*************/
/// @notice The address of L2ScrollMessenger contract.
address public messenger;
/***************
* Constructor *
***************/
constructor(address _owner) {
_transferOwnership(_owner);
}
/// @notice Initialize the state of `L2MessageQueue`
/// @dev You are not allowed to initialize when there are some messages appended.
/// @param _messenger The address of messenger to update.
function initialize(address _messenger) external onlyOwner {
require(nextMessageIndex == 0, "cannot initialize");
_initializeMerkleTree();
messenger = _messenger;
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @notice record the message to merkle tree and compute the new root.
/// @param _messageHash The hash of the new added message.
function appendMessage(bytes32 _messageHash) external returns (bytes32) {
require(msg.sender == messenger, "only messenger");
(uint256 _currentNonce, bytes32 _currentRoot) = _appendMessageHash(_messageHash);
// We can use the event to compute the merkle tree locally.
emit AppendMessage(_currentNonce, _messageHash);
return _currentRoot;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
library AddressAliasHelper {
/// @dev The offset added to the address in L1.
uint160 internal 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.16;
abstract contract AppendOnlyMerkleTree {
/// @dev The maximum height of the withdraw merkle tree.
uint256 private constant MAX_TREE_HEIGHT = 40;
/// @notice The merkle root of the current merkle tree.
/// @dev This is actual equal to `branches[n]`.
bytes32 public messageRoot;
/// @notice The next unused message index.
uint256 public nextMessageIndex;
/// @notice The list of zero hash in each height.
bytes32[MAX_TREE_HEIGHT] private zeroHashes;
/// @notice The list of minimum merkle proofs needed to compute next root.
/// @dev Only first `n` elements are used, where `n` is the minimum value that `2^{n-1} >= currentMaxNonce + 1`.
/// It means we only use `currentMaxNonce + 1` leaf nodes to construct the merkle tree.
bytes32[MAX_TREE_HEIGHT] public branches;
function _initializeMerkleTree() internal {
// Compute hashes in empty sparse Merkle tree
for (uint256 height = 0; height + 1 < MAX_TREE_HEIGHT; height++) {
zeroHashes[height + 1] = _efficientHash(zeroHashes[height], zeroHashes[height]);
}
}
function _appendMessageHash(bytes32 _messageHash) internal returns (uint256, bytes32) {
require(zeroHashes[1] != bytes32(0), "call before initialization");
uint256 _currentMessageIndex = nextMessageIndex;
bytes32 _hash = _messageHash;
uint256 _height = 0;
while (_currentMessageIndex != 0) {
if (_currentMessageIndex % 2 == 0) {
// it may be used in next round.
branches[_height] = _hash;
// it's a left child, the right child must be null
_hash = _efficientHash(_hash, zeroHashes[_height]);
} else {
// it's a right child, use previously computed hash
_hash = _efficientHash(branches[_height], _hash);
}
unchecked {
_height += 1;
}
_currentMessageIndex >>= 1;
}
branches[_height] = _hash;
messageRoot = _hash;
_currentMessageIndex = nextMessageIndex;
unchecked {
nextMessageIndex = _currentMessageIndex + 1;
}
return (_currentMessageIndex, _hash);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
abstract contract OwnableBase {
/**********
* Events *
**********/
/// @notice Emitted when owner is changed by current owner.
/// @param _oldOwner The address of previous owner.
/// @param _newOwner The address of new owner.
event OwnershipTransferred(address indexed _oldOwner, address indexed _newOwner);
/*************
* Variables *
*************/
/// @notice The address of the current owner.
address public owner;
/**********************
* Function Modifiers *
**********************/
/// @dev Throws if called by any account other than the owner.
modifier onlyOwner() {
require(owner == msg.sender, "caller is not the owner");
_;
}
/************************
* Restricted Functions *
************************/
/// @notice Leaves the contract without owner. It will not be possible to call
/// `onlyOwner` functions anymore. Can only be called by the current owner.
///
/// @dev Renouncing ownership will leave the contract without an owner,
/// thereby removing any functionality that is only available to the owner.
function renounceOwnership() public onlyOwner {
_transferOwnership(address(0));
}
/// @notice Transfers ownership of the contract to a new account (`newOwner`).
/// Can only be called by the current owner.
function transferOwnership(address _newOwner) public onlyOwner {
require(_newOwner != address(0), "new owner is the zero address");
_transferOwnership(_newOwner);
}
/**********************
* Internal Functions *
**********************/
/// @dev Transfers ownership of the contract to a new account (`newOwner`).
/// Internal function without access restriction.
function _transferOwnership(address _newOwner) internal {
address _oldOwner = owner;
owner = _newOwner;
emit OwnershipTransferred(_oldOwner, _newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
library ScrollConstants {
/// @notice The address of default cross chain message sender.
address internal constant DEFAULT_XDOMAIN_MESSAGE_SENDER = address(1);
/// @notice The address for dropping message.
/// @dev The first 20 bytes of keccak("drop")
address internal constant DROP_XDOMAIN_MESSAGE_SENDER = 0x6f297C61B5C92eF107fFD30CD56AFFE5A273e841;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
interface IScrollMessenger {
/**********
* Events *
**********/
/// @notice Emitted when a cross domain message is sent.
/// @param sender The address of the sender who initiates the message.
/// @param target The address of target contract to call.
/// @param value The amount of value passed to the target contract.
/// @param messageNonce The nonce of the message.
/// @param gasLimit The optional gas limit passed to L1 or L2.
/// @param message The calldata passed to the target contract.
event SentMessage(
address indexed sender,
address indexed target,
uint256 value,
uint256 messageNonce,
uint256 gasLimit,
bytes message
);
/// @notice Emitted when a cross domain message is relayed successfully.
/// @param messageHash The hash of the message.
event RelayedMessage(bytes32 indexed messageHash);
/// @notice Emitted when a cross domain message is failed to relay.
/// @param messageHash The hash of the message.
event FailedRelayedMessage(bytes32 indexed messageHash);
/**********
* Errors *
**********/
/// @dev Thrown when the given address is `address(0)`.
error ErrorZeroAddress();
/*************************
* Public View Functions *
*************************/
/// @notice Return the sender of a cross domain message.
function xDomainMessageSender() external view returns (address);
/*****************************
* Public Mutating Functions *
*****************************/
/// @notice Send cross chain message from L1 to L2 or L2 to L1.
/// @param target The address of account who receive the message.
/// @param value The amount of ether passed when call target contract.
/// @param message The content of the message.
/// @param gasLimit Gas limit required to complete the message relay on corresponding chain.
function sendMessage(
address target,
uint256 value,
bytes calldata message,
uint256 gasLimit
) external payable;
/// @notice Send cross chain message from L1 to L2 or L2 to L1.
/// @param target The address of account who receive the message.
/// @param value The amount of ether passed when call target contract.
/// @param message The content of the message.
/// @param gasLimit Gas limit required to complete the message relay on corresponding chain.
/// @param refundAddress The address of account who will receive the refunded fee.
function sendMessage(
address target,
uint256 value,
bytes calldata message,
uint256 gasLimit,
address refundAddress
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {ScrollConstants} from "./constants/ScrollConstants.sol";
import {IScrollMessenger} from "./IScrollMessenger.sol";
// solhint-disable var-name-mixedcase
abstract contract ScrollMessengerBase is
OwnableUpgradeable,
PausableUpgradeable,
ReentrancyGuardUpgradeable,
IScrollMessenger
{
/**********
* Events *
**********/
/// @notice Emitted when owner updates fee vault contract.
/// @param _oldFeeVault The address of old fee vault contract.
/// @param _newFeeVault The address of new fee vault contract.
event UpdateFeeVault(address _oldFeeVault, address _newFeeVault);
/*************
* Constants *
*************/
/// @notice The address of counterpart ScrollMessenger contract in L1/L2.
address public immutable counterpart;
/*************
* Variables *
*************/
/// @notice See {IScrollMessenger-xDomainMessageSender}
address public override xDomainMessageSender;
/// @dev The storage slot used as counterpart ScrollMessenger contract, which is deprecated now.
address private __counterpart;
/// @notice The address of fee vault, collecting cross domain messaging fee.
address public feeVault;
/// @dev The storage slot used as ETH rate limiter contract, which is deprecated now.
address private __rateLimiter;
/// @dev The storage slots for future usage.
uint256[46] private __gap;
/**********************
* Function Modifiers *
**********************/
modifier notInExecution() {
require(
xDomainMessageSender == ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER,
"Message is already in execution"
);
_;
}
/***************
* Constructor *
***************/
constructor(address _counterpart) {
if (_counterpart == address(0)) {
revert ErrorZeroAddress();
}
counterpart = _counterpart;
}
function __ScrollMessengerBase_init(address, address _feeVault) internal onlyInitializing {
OwnableUpgradeable.__Ownable_init();
PausableUpgradeable.__Pausable_init();
ReentrancyGuardUpgradeable.__ReentrancyGuard_init();
// initialize to a nonzero value
xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER;
if (_feeVault != address(0)) {
feeVault = _feeVault;
}
}
// make sure only owner can send ether to messenger to avoid possible user fund loss.
receive() external payable onlyOwner {}
/************************
* Restricted Functions *
************************/
/// @notice Update fee vault contract.
/// @dev This function can only called by contract owner.
/// @param _newFeeVault The address of new fee vault contract.
function updateFeeVault(address _newFeeVault) external onlyOwner {
address _oldFeeVault = feeVault;
feeVault = _newFeeVault;
emit UpdateFeeVault(_oldFeeVault, _newFeeVault);
}
/// @notice Pause the contract
/// @dev This function can only called by contract owner.
/// @param _status The pause status to update.
function setPause(bool _status) external onlyOwner {
if (_status) {
_pause();
} else {
_unpause();
}
}
/**********************
* Internal Functions *
**********************/
/// @dev Internal function to generate the correct cross domain calldata for a message.
/// @param _sender Message sender address.
/// @param _target Target contract address.
/// @param _value The amount of ETH pass to the target.
/// @param _messageNonce Nonce for the provided message.
/// @param _message Message to send to the target.
/// @return ABI encoded cross domain calldata.
function _encodeXDomainCalldata(
address _sender,
address _target,
uint256 _value,
uint256 _messageNonce,
bytes memory _message
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(address,address,uint256,uint256,bytes)",
_sender,
_target,
_value,
_messageNonce,
_message
);
}
/// @dev Internal function to check whether the `_target` address is allowed to avoid attack.
/// @param _target The address of target address to check.
function _validateTargetAddress(address _target) internal view {
// @note check more `_target` address to avoid attack in the future when we add more external contracts.
require(_target != address(this), "Forbid to call self");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
library PatriciaMerkleTrieVerifier {
/// @notice Internal function to validates a proof from eth_getProof.
/// @param account The address of the contract.
/// @param storageKey The storage slot to verify.
/// @param proof The rlp encoding result of eth_getProof.
/// @return stateRoot The computed state root. Must be checked by the caller.
/// @return storageValue The value of `storageKey`.
///
/// @dev The code is based on
/// 1. https://eips.ethereum.org/EIPS/eip-1186
/// 2. https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
/// 3. https://github.com/ethereum/go-ethereum/blob/master/trie/proof.go#L114
/// 4. https://github.com/privacy-scaling-explorations/zkevm-chain/blob/master/contracts/templates/PatriciaValidator.sol
///
/// The encoding order of `proof` is
/// ```text
/// | 1 byte | ... | 1 byte | ... |
/// | account proof length | account proof | storage proof length | storage proof |
/// ```
function verifyPatriciaProof(
address account,
bytes32 storageKey,
bytes calldata proof
) internal pure returns (bytes32 stateRoot, bytes32 storageValue) {
assembly {
// hashes 32 bytes of `v`
function keccak_32(v) -> r {
mstore(0x00, v)
r := keccak256(0x00, 0x20)
}
// hashes the last 20 bytes of `v`
function keccak_20(v) -> r {
mstore(0x00, v)
r := keccak256(0x0c, 0x14)
}
// reverts with error `msg`.
// make sure the length of error string <= 32
function revertWith(msg) {
// keccak("Error(string)")
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(0x04, 0x20) // str.offset
mstore(0x44, msg)
let msgLen
for {
} msg {
} {
msg := shl(8, msg)
msgLen := add(msgLen, 1)
}
mstore(0x24, msgLen) // str.length
revert(0x00, 0x64)
}
// reverts with `msg` when condition is not matched.
// make sure the length of error string <= 32
function require(cond, msg) {
if iszero(cond) {
revertWith(msg)
}
}
// special function for decoding the storage value
// because of the prefix truncation if value > 31 bytes
// see `loadValue`
function decodeItem(word, len) -> ret {
// default
ret := word
// RLP single byte
if lt(word, 0x80) {
leave
}
// truncated
if gt(len, 32) {
leave
}
// value is >= 0x80 and <= 32 bytes.
// `len` should be at least 2 (prefix byte + value)
// otherwise the RLP is malformed.
let bits := mul(len, 8)
// sub 8 bits - the prefix
bits := sub(bits, 8)
let mask := shl(bits, 0xff)
// invert the mask
mask := not(mask)
// should hold the value - prefix byte
ret := and(ret, mask)
}
// returns the `len` of the whole RLP list at `ptr`
// and the offset for the first value inside the list.
function decodeListLength(ptr) -> len, startOffset {
let b0 := byte(0, calldataload(ptr))
// In most cases, it is a long list. So we reorder the branch to reduce branch prediction miss.
// 0xf8 - 0xff, long list, length > 55
if gt(b0, 0xf7) {
// the RLP encoding consists of a single byte with value 0xf7
// plus the length in bytes of the length of the payload in binary form,
// followed by the length of the payload, followed by the concatenation
// of the RLP encodings of the items.
// the extended length is ignored
let lengthBytes := sub(b0, 0xf7)
if gt(lengthBytes, 32) {
invalid()
}
// load the extended length
startOffset := add(ptr, 1)
let extendedLen := calldataload(startOffset)
let bits := sub(256, mul(lengthBytes, 8))
extendedLen := shr(bits, extendedLen)
len := add(extendedLen, lengthBytes)
len := add(len, 1)
startOffset := add(startOffset, lengthBytes)
leave
}
// 0xc0 - 0xf7, short list, length <= 55
if gt(b0, 0xbf) {
// the RLP encoding consists of a single byte with value 0xc0
// plus the length of the list followed by the concatenation of
// the RLP encodings of the items.
len := sub(b0, 0xbf)
startOffset := add(ptr, 1)
leave
}
revertWith("Not list")
}
// returns the kind, calldata offset of the value and the length in bytes
// for the RLP encoded data item at `ptr`. used in `decodeFlat`
// kind = 0 means string/bytes, kind = 1 means list.
function decodeValue(ptr) -> kind, dataLen, valueOffset {
let b0 := byte(0, calldataload(ptr))
// 0x00 - 0x7f, single byte
if lt(b0, 0x80) {
// for a single byte whose value is in the [0x00, 0x7f] range,
// that byte is its own RLP encoding.
dataLen := 1
valueOffset := ptr
leave
}
// 0x80 - 0xb7, short string/bytes, length <= 55
if lt(b0, 0xb8) {
// the RLP encoding consists of a single byte with value 0x80
// plus the length of the string followed by the string.
dataLen := sub(b0, 0x80)
valueOffset := add(ptr, 1)
leave
}
// 0xb8 - 0xbf, long string/bytes, length > 55
if lt(b0, 0xc0) {
// the RLP encoding consists of a single byte with value 0xb7
// plus the length in bytes of the length of the string in binary form,
// followed by the length of the string, followed by the string.
let lengthBytes := sub(b0, 0xb7)
if gt(lengthBytes, 4) {
invalid()
}
// load the extended length
valueOffset := add(ptr, 1)
let extendedLen := calldataload(valueOffset)
let bits := sub(256, mul(lengthBytes, 8))
extendedLen := shr(bits, extendedLen)
dataLen := extendedLen
valueOffset := add(valueOffset, lengthBytes)
leave
}
kind := 1
// 0xc0 - 0xf7, short list, length <= 55
if lt(b0, 0xf8) {
// intentionally ignored
// dataLen := sub(firstByte, 0xc0)
valueOffset := add(ptr, 1)
leave
}
// 0xf8 - 0xff, long list, length > 55
{
// the extended length is ignored
dataLen := sub(b0, 0xf7)
valueOffset := add(ptr, 1)
leave
}
}
// decodes all RLP encoded data and stores their DATA items
// [length - 128 bits | calldata offset - 128 bits] in a continous memory region.
// Expects that the RLP starts with a list that defines the length
// of the whole RLP region.
function decodeFlat(_ptr) -> ptr, memStart, nItems, hash {
ptr := _ptr
// load free memory ptr
// doesn't update the ptr and leaves the memory region dirty
memStart := mload(0x40)
let payloadLen, startOffset := decodeListLength(ptr)
// reuse memStart region and hash
calldatacopy(memStart, ptr, payloadLen)
hash := keccak256(memStart, payloadLen)
let memPtr := memStart
let ptrStop := add(ptr, payloadLen)
ptr := startOffset
// decode until the end of the list
for {
} lt(ptr, ptrStop) {
} {
let kind, len, valuePtr := decodeValue(ptr)
ptr := add(len, valuePtr)
if iszero(kind) {
// store the length of the data and the calldata offset
// low -------> high
// | 128 bits | 128 bits |
// | calldata offset | value length |
mstore(memPtr, or(shl(128, len), valuePtr))
memPtr := add(memPtr, 0x20)
}
}
if iszero(eq(ptr, ptrStop)) {
invalid()
}
nItems := div(sub(memPtr, memStart), 32)
}
// prefix gets truncated to 256 bits
// `depth` is untrusted and can lead to bogus
// shifts/masks. In that case, the remaining verification
// steps must fail or lead to an invalid stateRoot hash
// if the proof data is 'spoofed but valid'
function derivePath(key, depth) -> path {
path := key
let bits := mul(depth, 4)
{
let mask := not(0)
mask := shr(bits, mask)
path := and(path, mask)
}
// even prefix
let prefix := 0x20
if mod(depth, 2) {
// odd
prefix := 0x3
}
// the prefix may be shifted outside bounds
// this is intended, see `loadValue`
bits := sub(256, bits)
prefix := shl(bits, prefix)
path := or(prefix, path)
}
// loads and aligns a value from calldata
// given the `len|offset` stored at `memPtr`
function loadValue(memPtr, idx) -> value {
let tmp := mload(add(memPtr, mul(32, idx)))
// assuming 0xffffff is sufficient for storing calldata offset
let offset := and(tmp, 0xffffff)
let len := shr(128, tmp)
if gt(len, 31) {
// special case - truncating the value is intended.
// this matches the behavior in `derivePath` that truncates to 256 bits.
offset := add(offset, sub(len, 32))
value := calldataload(offset)
leave
}
// everything else is
// < 32 bytes - align the value
let bits := mul(sub(32, len), 8)
value := calldataload(offset)
value := shr(bits, value)
}
// loads and aligns a value from calldata
// given the `len|offset` stored at `memPtr`
// Same as `loadValue` except it returns also the size
// of the value.
function loadValueLen(memPtr, idx) -> value, len {
let tmp := mload(add(memPtr, mul(32, idx)))
// assuming 0xffffff is sufficient for storing calldata offset
let offset := and(tmp, 0xffffff)
len := shr(128, tmp)
if gt(len, 31) {
// special case - truncating the value is intended.
// this matches the behavior in `derivePath` that truncates to 256 bits.
offset := add(offset, sub(len, 32))
value := calldataload(offset)
leave
}
// everything else is
// < 32 bytes - align the value
let bits := mul(sub(32, len), 8)
value := calldataload(offset)
value := shr(bits, value)
}
function loadPair(memPtr, idx) -> offset, len {
let tmp := mload(add(memPtr, mul(32, idx)))
// assuming 0xffffff is sufficient for storing calldata offset
offset := and(tmp, 0xffffff)
len := shr(128, tmp)
}
// decodes RLP at `_ptr`.
// reverts if the number of DATA items doesn't match `nValues`.
// returns the RLP data items at pos `v0`, `v1`
// and the size of `v1out`
function hashCompareSelect(_ptr, nValues, v0, v1) -> ptr, hash, v0out, v1out, v1outlen {
ptr := _ptr
let memStart, nItems
ptr, memStart, nItems, hash := decodeFlat(ptr)
if iszero(eq(nItems, nValues)) {
revertWith("Node items mismatch")
}
v0out, v1outlen := loadValueLen(memStart, v0)
v1out, v1outlen := loadValueLen(memStart, v1)
}
// traverses the tree from the root to the node before the leaf.
// based on https://github.com/ethereum/go-ethereum/blob/master/trie/proof.go#L114
function walkTree(key, _ptr) -> ptr, rootHash, expectedHash, path {
ptr := _ptr
// the first byte is the number of nodes
let nodes := byte(0, calldataload(ptr))
ptr := add(ptr, 1)
// keeps track of ascend/descend - however you may look at a tree
let depth
// treat the leaf node with different logic
for {
let i := 1
} lt(i, nodes) {
i := add(i, 1)
} {
let memStart, nItems, hash
ptr, memStart, nItems, hash := decodeFlat(ptr)
// first item is considered the root node.
// Otherwise verifies that the hash of the current node
// is the same as the previous choosen one.
switch i
case 1 {
rootHash := hash
}
default {
require(eq(hash, expectedHash), "Hash mismatch")
}
switch nItems
case 2 {
// extension node
// load the second item.
// this is the hash of the next node.
let value, len := loadValueLen(memStart, 1)
expectedHash := value
// get the byte length of the first item
// Note: the value itself is not validated
// and it is instead assumed that any invalid
// value is invalidated by comparing the root hash.
let offset := mload(memStart)
let prefixLen := shr(128, offset)
// assuming 0xffffff is sufficient for storing calldata offset
offset := and(offset, 0xffffff)
let flag := shr(252, calldataload(offset))
switch flag
case 0 {
// extension with even length
depth := add(depth, mul(2, sub(prefixLen, 1)))
}
case 1 {
// extension with odd length
depth := add(depth, sub(mul(2, prefixLen), 1))
}
default {
// everything else is unexpected
revertWith("Invalid extension node")
}
}
case 17 {
let bits := sub(252, mul(depth, 4))
let nibble := and(shr(bits, key), 0xf)
// load the value at pos `nibble`
let value, len := loadValueLen(memStart, nibble)
expectedHash := value
depth := add(depth, 1)
}
default {
// everything else is unexpected
revertWith("Invalid node")
}
}
// lastly, derive the path of the choosen one (TM)
path := derivePath(key, depth)
}
// shared variable names
let storageHash
let encodedPath
let path
let hash
let vlen
// starting point
let ptr := proof.offset
{
// account proof
// Note: this doesn't work if there are no intermediate nodes before the leaf.
// This is not possible in practice because of the fact that there must be at least
// 2 accounts in the tree to make a transaction to a existing contract possible.
// Thus, 2 leaves.
let prevHash
let key := keccak_20(account)
// `stateRoot` is a return value and must be checked by the caller
ptr, stateRoot, prevHash, path := walkTree(key, ptr)
let memStart, nItems
ptr, memStart, nItems, hash := decodeFlat(ptr)
// the hash of the leaf must match the previous hash from the node
require(eq(hash, prevHash), "Account leaf hash mismatch")
// 2 items
// - encoded path
// - account leaf RLP (4 items)
require(eq(nItems, 2), "Account leaf node mismatch")
encodedPath := loadValue(memStart, 0)
// the calculated path must match the encoded path in the leaf
require(eq(path, encodedPath), "Account encoded path mismatch")
// Load the position, length of the second element (RLP encoded)
let leafPtr, leafLen := loadPair(memStart, 1)
leafPtr, memStart, nItems, hash := decodeFlat(leafPtr)
// the account leaf should contain 4 values,
// we want:
// - storageHash @ 2
require(eq(nItems, 4), "Account leaf items mismatch")
storageHash := loadValue(memStart, 2)
}
{
// storage proof
let rootHash
let key := keccak_32(storageKey)
ptr, rootHash, hash, path := walkTree(key, ptr)
// leaf should contain 2 values
// - encoded path @ 0
// - storageValue @ 1
ptr, hash, encodedPath, storageValue, vlen := hashCompareSelect(ptr, 2, 0, 1)
// the calculated path must match the encoded path in the leaf
require(eq(path, encodedPath), "Storage encoded path mismatch")
switch rootHash
case 0 {
// in the case that the leaf is the only element, then
// the hash of the leaf must match the value from the account leaf
require(eq(hash, storageHash), "Storage root mismatch")
}
default {
// otherwise the root hash of the storage tree
// must match the value from the account leaf
require(eq(rootHash, storageHash), "Storage root mismatch")
}
// storageValue is a return value
storageValue := decodeItem(storageValue, vlen)
}
// the one and only boundary check
// in case an attacker crafted a malicous payload
// and succeeds in the prior verification steps
// then this should catch any bogus accesses
if iszero(eq(ptr, add(proof.offset, proof.length))) {
revertWith("Proof length mismatch")
}
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"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":[{"internalType":"address","name":"_counterpart","type":"address"},{"internalType":"address","name":"_messageQueue","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrorZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"FailedRelayedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"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":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"RelayedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"messageNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"}],"name":"SentMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldFeeVault","type":"address"},{"indexed":false,"internalType":"address","name":"_newFeeVault","type":"address"}],"name":"UpdateFeeVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMaxFailedExecutionTimes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxFailedExecutionTimes","type":"uint256"}],"name":"UpdateMaxFailedExecutionTimes","type":"event"},{"inputs":[],"name":"counterpart","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"isL1MessageExecuted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"messageSendTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"relayMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_message","type":"bytes"},{"internalType":"uint256","name":"_gasLimit","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"sendMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_message","type":"bytes"},{"internalType":"uint256","name":"_gasLimit","type":"uint256"}],"name":"sendMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"_status","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newFeeVault","type":"address"}],"name":"updateFeeVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"xDomainMessageSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c06040523480156200001157600080fd5b50604051620015c5380380620015c5833981016040819052620000349162000186565b816001600160a01b0381166200005d5760405163a7f9319d60e01b815260040160405180910390fd5b6001600160a01b0390811660805281166200008b5760405163a7f9319d60e01b815260040160405180910390fd5b62000095620000a8565b6001600160a01b031660a05250620001be565b600054610100900460ff1615620001155760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161462000167576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b80516001600160a01b03811681146200018157600080fd5b919050565b600080604083850312156200019a57600080fd5b620001a58362000169565b9150620001b56020840162000169565b90509250929050565b60805160a0516113c562000200600039600081816101820152818161081d015281816109320152610aa901526000818161024e015261049601526113c56000f3fe6080604052600436106100f75760003560e01c8063797594b01161008a578063bedb86fb11610059578063bedb86fb146102c1578063c4d66de8146102e1578063e70fc93b14610301578063f2fde38b1461033c57600080fd5b8063797594b01461023c5780638da5cb5b146102705780638ef1332e1461028e578063b2267a7b146102ae57600080fd5b80635c975abb116100c65780635c975abb146101dc5780635f7b1577146101f45780636e296e4514610207578063715018a61461022757600080fd5b806302345b501461010b5780632a6cccb2146101505780633b70c18a14610170578063478222c2146101bc57600080fd5b366101065761010461035c565b005b600080fd5b34801561011757600080fd5b5061013b610126366004610fb8565b60fc6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b34801561015c57600080fd5b5061010461016b366004610fed565b6103bd565b34801561017c57600080fd5b506101a47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610147565b3480156101c857600080fd5b5060cb546101a4906001600160a01b031681565b3480156101e857600080fd5b5060655460ff1661013b565b61010461020236600461100f565b610427565b34801561021357600080fd5b5060c9546101a4906001600160a01b031681565b34801561023357600080fd5b5061010461047a565b34801561024857600080fd5b506101a47f000000000000000000000000000000000000000000000000000000000000000081565b34801561027c57600080fd5b506033546001600160a01b03166101a4565b34801561029a57600080fd5b506101046102a9366004611158565b61048c565b6101046102bc3660046111ca565b6105c5565b3480156102cd57600080fd5b506101046102dc366004611229565b6105df565b3480156102ed57600080fd5b506101046102fc366004610fed565b610600565b34801561030d57600080fd5b5061032e61031c366004610fb8565b60fb6020526000908152604090205481565b604051908152602001610147565b34801561034857600080fd5b50610104610357366004610fed565b610711565b6033546001600160a01b031633146103bb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b565b6103c561035c565b60cb80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f4aadc32827849f797733838c61302f7f56d2b6db28caa175eb3f7f8e5aba25f591015b60405180910390a15050565b61042f610787565b610472868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506107cd915050565b505050505050565b61048261035c565b6103bb6000610a05565b610494610787565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167311110000000000000000000000000000000011101933016001600160a01b03161461052c5760405162461bcd60e51b815260206004820152601f60248201527f43616c6c6572206973206e6f74204c315363726f6c6c4d657373656e6765720060448201526064016103b2565b600061053b8686868686610a57565b8051602091820120600081815260fc90925260409091205490915060ff16156105b85760405162461bcd60e51b815260206004820152602960248201527f4d6573736167652077617320616c7265616479207375636365737366756c6c7960448201526808195e1958dd5d195960ba1b60648201526084016103b2565b6104728686868585610aa7565b6105cd610787565b6105d9848484846107cd565b50505050565b6105e761035c565b80156105f8576105f5610c95565b50565b6105f5610cef565b600054610100900460ff16158080156106205750600054600160ff909116105b8061063a5750303b15801561063a575060005460ff166001145b61069d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103b2565b6000805460ff1916600117905580156106c0576000805461ff0019166101001790555b6106cb600080610d28565b801561070d576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161041b565b5050565b61071961035c565b6001600160a01b03811661077e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103b2565b6105f581610a05565b60655460ff16156103bb5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016103b2565b6107d5610daa565b8234146108195760405162461bcd60e51b81526020600482015260126024820152710dae6ce5cecc2d8eaca40dad2e6dac2e8c6d60731b60448201526064016103b2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166326aad7b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610879573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089d919061124b565b905060006108ae3387878588610a57565b8051602091820120600081815260fb9092526040909120549091501561090b5760405162461bcd60e51b81526020600482015260126024820152714475706c696361746564206d65737361676560701b60448201526064016103b2565b600081815260fb6020526040908190204290555163600a2e7760e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063600a2e77906024016020604051808303816000875af1158015610983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a7919061124b565b506001600160a01b038616336001600160a01b03167f104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e878587896040516109f194939291906112b4565b60405180910390a350506105d96001609755565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60608585858585604051602401610a729594939291906112e3565b60408051601f198184030181529190526020810180516001600160e01b0316634778999760e11b179052905095945050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603610b285760405162461bcd60e51b815260206004820152601c60248201527f466f7262696420746f2063616c6c206d6573736167652071756575650000000060448201526064016103b2565b610b3184610e0a565b60c9546001600160a01b0390811690861603610b885760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b21036b2b9b9b0b3b29039b2b73232b960511b60448201526064016103b2565b60c980546001600160a01b0319166001600160a01b03878116919091179091556040516000918616908590610bbe908690611328565b60006040518083038185875af1925050503d8060008114610bfb576040519150601f19603f3d011682016040523d82523d6000602084013e610c00565b606091505b505060c980546001600160a01b031916600117905590508015610c6257600082815260fc6020526040808220805460ff191660011790555183917f4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c91a2610472565b60405182907f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f90600090a2505050505050565b610c9d610787565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610cd23390565b6040516001600160a01b03909116815260200160405180910390a1565b610cf7610e58565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33610cd2565b600054610100900460ff16610d4f5760405162461bcd60e51b81526004016103b290611344565b610d57610ea1565b610d5f610ed0565b610d67610eff565b60c980546001600160a01b03191660011790556001600160a01b0381161561070d5760cb80546001600160a01b0383166001600160a01b03199091161790555050565b600260975403610dfc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016103b2565b6002609755565b6001609755565b306001600160a01b038216036105f55760405162461bcd60e51b81526020600482015260136024820152722337b93134b2103a379031b0b6361039b2b63360691b60448201526064016103b2565b60655460ff166103bb5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016103b2565b600054610100900460ff16610ec85760405162461bcd60e51b81526004016103b290611344565b6103bb610f2e565b600054610100900460ff16610ef75760405162461bcd60e51b81526004016103b290611344565b6103bb610f5e565b600054610100900460ff16610f265760405162461bcd60e51b81526004016103b290611344565b6103bb610f91565b600054610100900460ff16610f555760405162461bcd60e51b81526004016103b290611344565b6103bb33610a05565b600054610100900460ff16610f855760405162461bcd60e51b81526004016103b290611344565b6065805460ff19169055565b600054610100900460ff16610e035760405162461bcd60e51b81526004016103b290611344565b600060208284031215610fca57600080fd5b5035919050565b80356001600160a01b0381168114610fe857600080fd5b919050565b600060208284031215610fff57600080fd5b61100882610fd1565b9392505050565b60008060008060008060a0878903121561102857600080fd5b61103187610fd1565b955060208701359450604087013567ffffffffffffffff8082111561105557600080fd5b818901915089601f83011261106957600080fd5b81358181111561107857600080fd5b8a602082850101111561108a57600080fd5b602083019650809550505050606087013591506110a960808801610fd1565b90509295509295509295565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126110dc57600080fd5b813567ffffffffffffffff808211156110f7576110f76110b5565b604051601f8301601f19908116603f0116810190828211818310171561111f5761111f6110b5565b8160405283815286602085880101111561113857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600060a0868803121561117057600080fd5b61117986610fd1565b945061118760208701610fd1565b93506040860135925060608601359150608086013567ffffffffffffffff8111156111b157600080fd5b6111bd888289016110cb565b9150509295509295909350565b600080600080608085870312156111e057600080fd5b6111e985610fd1565b935060208501359250604085013567ffffffffffffffff81111561120c57600080fd5b611218878288016110cb565b949793965093946060013593505050565b60006020828403121561123b57600080fd5b8135801515811461100857600080fd5b60006020828403121561125d57600080fd5b5051919050565b60005b8381101561127f578181015183820152602001611267565b50506000910152565b600081518084526112a0816020860160208601611264565b601f01601f19169290920160200192915050565b8481528360208201528260408201526080606082015260006112d96080830184611288565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061131d90830184611288565b979650505050505050565b6000825161133a818460208701611264565b9190910192915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fea2646970667358221220a1f6956ae968d8877a11a932f9805f5b1f71b3f1c38bbee4168ba20e975820f264736f6c634300081000330000000000000000000000006774bcbd5cecef1336b5300fb5186a12ddd8b3670000000000000000000000005300000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106100f75760003560e01c8063797594b01161008a578063bedb86fb11610059578063bedb86fb146102c1578063c4d66de8146102e1578063e70fc93b14610301578063f2fde38b1461033c57600080fd5b8063797594b01461023c5780638da5cb5b146102705780638ef1332e1461028e578063b2267a7b146102ae57600080fd5b80635c975abb116100c65780635c975abb146101dc5780635f7b1577146101f45780636e296e4514610207578063715018a61461022757600080fd5b806302345b501461010b5780632a6cccb2146101505780633b70c18a14610170578063478222c2146101bc57600080fd5b366101065761010461035c565b005b600080fd5b34801561011757600080fd5b5061013b610126366004610fb8565b60fc6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b34801561015c57600080fd5b5061010461016b366004610fed565b6103bd565b34801561017c57600080fd5b506101a47f000000000000000000000000530000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610147565b3480156101c857600080fd5b5060cb546101a4906001600160a01b031681565b3480156101e857600080fd5b5060655460ff1661013b565b61010461020236600461100f565b610427565b34801561021357600080fd5b5060c9546101a4906001600160a01b031681565b34801561023357600080fd5b5061010461047a565b34801561024857600080fd5b506101a47f0000000000000000000000006774bcbd5cecef1336b5300fb5186a12ddd8b36781565b34801561027c57600080fd5b506033546001600160a01b03166101a4565b34801561029a57600080fd5b506101046102a9366004611158565b61048c565b6101046102bc3660046111ca565b6105c5565b3480156102cd57600080fd5b506101046102dc366004611229565b6105df565b3480156102ed57600080fd5b506101046102fc366004610fed565b610600565b34801561030d57600080fd5b5061032e61031c366004610fb8565b60fb6020526000908152604090205481565b604051908152602001610147565b34801561034857600080fd5b50610104610357366004610fed565b610711565b6033546001600160a01b031633146103bb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b565b6103c561035c565b60cb80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f4aadc32827849f797733838c61302f7f56d2b6db28caa175eb3f7f8e5aba25f591015b60405180910390a15050565b61042f610787565b610472868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506107cd915050565b505050505050565b61048261035c565b6103bb6000610a05565b610494610787565b7f0000000000000000000000006774bcbd5cecef1336b5300fb5186a12ddd8b3676001600160a01b03167311110000000000000000000000000000000011101933016001600160a01b03161461052c5760405162461bcd60e51b815260206004820152601f60248201527f43616c6c6572206973206e6f74204c315363726f6c6c4d657373656e6765720060448201526064016103b2565b600061053b8686868686610a57565b8051602091820120600081815260fc90925260409091205490915060ff16156105b85760405162461bcd60e51b815260206004820152602960248201527f4d6573736167652077617320616c7265616479207375636365737366756c6c7960448201526808195e1958dd5d195960ba1b60648201526084016103b2565b6104728686868585610aa7565b6105cd610787565b6105d9848484846107cd565b50505050565b6105e761035c565b80156105f8576105f5610c95565b50565b6105f5610cef565b600054610100900460ff16158080156106205750600054600160ff909116105b8061063a5750303b15801561063a575060005460ff166001145b61069d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103b2565b6000805460ff1916600117905580156106c0576000805461ff0019166101001790555b6106cb600080610d28565b801561070d576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161041b565b5050565b61071961035c565b6001600160a01b03811661077e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103b2565b6105f581610a05565b60655460ff16156103bb5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016103b2565b6107d5610daa565b8234146108195760405162461bcd60e51b81526020600482015260126024820152710dae6ce5cecc2d8eaca40dad2e6dac2e8c6d60731b60448201526064016103b2565b60007f00000000000000000000000053000000000000000000000000000000000000006001600160a01b03166326aad7b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610879573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089d919061124b565b905060006108ae3387878588610a57565b8051602091820120600081815260fb9092526040909120549091501561090b5760405162461bcd60e51b81526020600482015260126024820152714475706c696361746564206d65737361676560701b60448201526064016103b2565b600081815260fb6020526040908190204290555163600a2e7760e01b8152600481018290527f00000000000000000000000053000000000000000000000000000000000000006001600160a01b03169063600a2e77906024016020604051808303816000875af1158015610983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a7919061124b565b506001600160a01b038616336001600160a01b03167f104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e878587896040516109f194939291906112b4565b60405180910390a350506105d96001609755565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60608585858585604051602401610a729594939291906112e3565b60408051601f198184030181529190526020810180516001600160e01b0316634778999760e11b179052905095945050505050565b7f00000000000000000000000053000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603610b285760405162461bcd60e51b815260206004820152601c60248201527f466f7262696420746f2063616c6c206d6573736167652071756575650000000060448201526064016103b2565b610b3184610e0a565b60c9546001600160a01b0390811690861603610b885760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b21036b2b9b9b0b3b29039b2b73232b960511b60448201526064016103b2565b60c980546001600160a01b0319166001600160a01b03878116919091179091556040516000918616908590610bbe908690611328565b60006040518083038185875af1925050503d8060008114610bfb576040519150601f19603f3d011682016040523d82523d6000602084013e610c00565b606091505b505060c980546001600160a01b031916600117905590508015610c6257600082815260fc6020526040808220805460ff191660011790555183917f4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c91a2610472565b60405182907f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f90600090a2505050505050565b610c9d610787565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610cd23390565b6040516001600160a01b03909116815260200160405180910390a1565b610cf7610e58565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33610cd2565b600054610100900460ff16610d4f5760405162461bcd60e51b81526004016103b290611344565b610d57610ea1565b610d5f610ed0565b610d67610eff565b60c980546001600160a01b03191660011790556001600160a01b0381161561070d5760cb80546001600160a01b0383166001600160a01b03199091161790555050565b600260975403610dfc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016103b2565b6002609755565b6001609755565b306001600160a01b038216036105f55760405162461bcd60e51b81526020600482015260136024820152722337b93134b2103a379031b0b6361039b2b63360691b60448201526064016103b2565b60655460ff166103bb5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016103b2565b600054610100900460ff16610ec85760405162461bcd60e51b81526004016103b290611344565b6103bb610f2e565b600054610100900460ff16610ef75760405162461bcd60e51b81526004016103b290611344565b6103bb610f5e565b600054610100900460ff16610f265760405162461bcd60e51b81526004016103b290611344565b6103bb610f91565b600054610100900460ff16610f555760405162461bcd60e51b81526004016103b290611344565b6103bb33610a05565b600054610100900460ff16610f855760405162461bcd60e51b81526004016103b290611344565b6065805460ff19169055565b600054610100900460ff16610e035760405162461bcd60e51b81526004016103b290611344565b600060208284031215610fca57600080fd5b5035919050565b80356001600160a01b0381168114610fe857600080fd5b919050565b600060208284031215610fff57600080fd5b61100882610fd1565b9392505050565b60008060008060008060a0878903121561102857600080fd5b61103187610fd1565b955060208701359450604087013567ffffffffffffffff8082111561105557600080fd5b818901915089601f83011261106957600080fd5b81358181111561107857600080fd5b8a602082850101111561108a57600080fd5b602083019650809550505050606087013591506110a960808801610fd1565b90509295509295509295565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126110dc57600080fd5b813567ffffffffffffffff808211156110f7576110f76110b5565b604051601f8301601f19908116603f0116810190828211818310171561111f5761111f6110b5565b8160405283815286602085880101111561113857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600060a0868803121561117057600080fd5b61117986610fd1565b945061118760208701610fd1565b93506040860135925060608601359150608086013567ffffffffffffffff8111156111b157600080fd5b6111bd888289016110cb565b9150509295509295909350565b600080600080608085870312156111e057600080fd5b6111e985610fd1565b935060208501359250604085013567ffffffffffffffff81111561120c57600080fd5b611218878288016110cb565b949793965093946060013593505050565b60006020828403121561123b57600080fd5b8135801515811461100857600080fd5b60006020828403121561125d57600080fd5b5051919050565b60005b8381101561127f578181015183820152602001611267565b50506000910152565b600081518084526112a0816020860160208601611264565b601f01601f19169290920160200192915050565b8481528360208201528260408201526080606082015260006112d96080830184611288565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061131d90830184611288565b979650505050505050565b6000825161133a818460208701611264565b9190910192915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fea2646970667358221220a1f6956ae968d8877a11a932f9805f5b1f71b3f1c38bbee4168ba20e975820f264736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006774bcbd5cecef1336b5300fb5186a12ddd8b3670000000000000000000000005300000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _counterpart (address): 0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367
Arg [1] : _messageQueue (address): 0x5300000000000000000000000000000000000000
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000006774bcbd5cecef1336b5300fb5186a12ddd8b367
Arg [1] : 0000000000000000000000005300000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 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.