ETH Price: $2,934.85 (-0.74%)

Contract

0x01FFa0003a21F14e039d249af4f4d770f8403B6D

Overview

ETH Balance

Linea Mainnet LogoLinea Mainnet LogoLinea Mainnet Logo0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
282897232026-01-25 8:19:221 hr ago1769329162
0x01FFa000...0f8403B6D
0 ETH
282866022026-01-25 6:18:523 hrs ago1769321932
0x01FFa000...0f8403B6D
0 ETH
282833912026-01-25 4:19:105 hrs ago1769314750
0x01FFa000...0f8403B6D
0 ETH
282801842026-01-25 2:21:247 hrs ago1769307684
0x01FFa000...0f8403B6D
0 ETH
282769872026-01-25 0:18:569 hrs ago1769300336
0x01FFa000...0f8403B6D
0 ETH
282740472026-01-24 22:18:2411 hrs ago1769293104
0x01FFa000...0f8403B6D
0 ETH
282710252026-01-24 20:22:2413 hrs ago1769286144
0x01FFa000...0f8403B6D
0 ETH
282683232026-01-24 18:19:2615 hrs ago1769278766
0x01FFa000...0f8403B6D
0 ETH
282673172026-01-24 17:40:2816 hrs ago1769276428
0x01FFa000...0f8403B6D
0.00102858 ETH
282673172026-01-24 17:40:2816 hrs ago1769276428
0x01FFa000...0f8403B6D
0 ETH
282651602026-01-24 16:22:4017 hrs ago1769271760
0x01FFa000...0f8403B6D
0 ETH
282647442026-01-24 16:08:2017 hrs ago1769270900
0x01FFa000...0f8403B6D
0 ETH
282615912026-01-24 14:18:3419 hrs ago1769264314
0x01FFa000...0f8403B6D
0 ETH
282582432026-01-24 12:19:1421 hrs ago1769257154
0x01FFa000...0f8403B6D
0 ETH
282549272026-01-24 10:18:2623 hrs ago1769249906
0x01FFa000...0f8403B6D
0 ETH
282516252026-01-24 8:19:2825 hrs ago1769242768
0x01FFa000...0f8403B6D
0 ETH
282456962026-01-24 4:18:2629 hrs ago1769228306
0x01FFa000...0f8403B6D
0 ETH
282426042026-01-24 2:19:0431 hrs ago1769221144
0x01FFa000...0f8403B6D
0 ETH
282393012026-01-24 0:19:1033 hrs ago1769213950
0x01FFa000...0f8403B6D
0 ETH
282363832026-01-23 22:18:3435 hrs ago1769206714
0x01FFa000...0f8403B6D
0 ETH
282331912026-01-23 20:18:4637 hrs ago1769199526
0x01FFa000...0f8403B6D
0 ETH
282299282026-01-23 18:18:2639 hrs ago1769192306
0x01FFa000...0f8403B6D
0 ETH
282270712026-01-23 16:33:3641 hrs ago1769186016
0x01FFa000...0f8403B6D
0 ETH
282266632026-01-23 16:18:4641 hrs ago1769185126
0x01FFa000...0f8403B6D
0 ETH
282250372026-01-23 15:19:1042 hrs ago1769181550
0x01FFa000...0f8403B6D
0 ETH
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SpokePortal

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
london EvmVersion
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { ISpokeMTokenLike } from "./interfaces/ISpokeMTokenLike.sol";
import { IRegistrarLike } from "./interfaces/IRegistrarLike.sol";
import { ISpokePortal } from "./interfaces/ISpokePortal.sol";
import { IPortal } from "./interfaces/IPortal.sol";

import { Portal } from "./Portal.sol";
import { PayloadType, PayloadEncoder } from "./libs/PayloadEncoder.sol";

/**
 * @title  SpokePortal
 * @author M^0 Labs
 * @notice Deployed on Spoke chains and responsible for sending and receiving M tokens
 *         as well as updating M index and Registrar keys.
 * @dev    Tokens are bridged using mint-burn mechanism.
 */
contract SpokePortal is Portal, ISpokePortal {
    using PayloadEncoder for bytes;

    /**
     * @notice Constructs SpokePortal Implementation contract
     * @dev    Sets immutable storage.
     * @param  mToken_       The address of M token.
     * @param  registrar_    The address of Registrar.
     * @param  swapFacility_ The address of Swap Facility.
     */
    constructor(address mToken_, address registrar_, address swapFacility_) Portal(mToken_, registrar_, swapFacility_) { }

    /// @inheritdoc IPortal
    function initialize(address bridge_, address initialOwner_, address initialPauser_) external initializer {
        _initialize(bridge_, initialOwner_, initialPauser_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                INTERNAL/PRIVATE INTERACTIVE FUNCTIONS                 //
    ///////////////////////////////////////////////////////////////////////////

    function _receiveCustomPayload(PayloadType payloadType_, bytes memory payload_) internal override {
        if (payloadType_ == PayloadType.Index) {
            _updateMTokenIndex(payload_);
        } else if (payloadType_ == PayloadType.Key) {
            _setRegistrarKey(payload_);
        } else if (payloadType_ == PayloadType.List) {
            _updateRegistrarList(payload_);
        }
    }

    /// @notice Updates M Token index to the index received from the remote chain.
    function _updateMTokenIndex(bytes memory payload_) private {
        uint128 index_ = payload_.decodeIndex();

        emit MTokenIndexReceived(index_);

        if (index_ > _currentIndex()) {
            ISpokeMTokenLike(mToken).updateIndex(index_);
        }
    }

    /// @notice Sets a Registrar key received from the Hub chain.
    function _setRegistrarKey(bytes memory payload_) private {
        (bytes32 key_, bytes32 value_) = payload_.decodeKey();

        emit RegistrarKeyReceived(key_, value_);

        IRegistrarLike(registrar).setKey(key_, value_);
    }

    /// @notice Adds or removes an account from the Registrar List based on the message from the Hub chain.
    function _updateRegistrarList(bytes memory payload_) private {
        (bytes32 listName_, address account_, bool add_) = payload_.decodeListUpdate();

        emit RegistrarListStatusReceived(listName_, account_, add_);

        if (add_) {
            IRegistrarLike(registrar).addToList(listName_, account_);
        } else {
            IRegistrarLike(registrar).removeFromList(listName_, account_);
        }
    }

    /**
     * @dev Mints M Token to the `recipient_`.
     * @param recipient_ The account to mint M tokens to.
     * @param amount_    The amount of M Token to mint to the recipient.
     * @param index_     The index from the source chain.
     */
    function _mintOrUnlock(uint256, address recipient_, uint256 amount_, uint128 index_) internal override {
        // Update M token index only if the index received from the remote chain is bigger
        if (index_ > _currentIndex()) {
            ISpokeMTokenLike(mToken).mint(recipient_, amount_, index_);
        } else {
            ISpokeMTokenLike(mToken).mint(recipient_, amount_);
        }
    }

    /**
     * @dev Burns M Token.
     * @param amount_ The amount of M Token to burn from the SpokePortal.
     */
    function _burnOrLock(uint256, uint256 amount_) internal override {
        ISpokeMTokenLike(mToken).burn(amount_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                 INTERNAL/PRIVATE VIEW/PURE FUNCTIONS                  //
    ///////////////////////////////////////////////////////////////////////////

    /// @dev Returns the current M token index used by the Spoke Portal.
    function _currentIndex() internal view override returns (uint128) {
        return ISpokeMTokenLike(mToken).currentIndex();
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { IMTokenLike } from "./IMTokenLike.sol";

/**
 * @title  Subset of Spoke M Token interface required for `SpokePortal` contract.
 * @author M^0 Labs
 */
interface ISpokeMTokenLike is IMTokenLike {
    /**
     * @notice Mints tokens.
     * @dev    MUST only be callable by the SpokePortal.
     * @param  account The address of account to mint to.
     * @param  amount  The amount of M Token to mint.
     */
    function mint(address account, uint256 amount) external;

    /**
     * @notice Updates the index and mints tokens.
     * @dev    MUST only be callable by the SpokePortal.
     * @param  account The address of account to mint to.
     * @param  amount  The amount of M Token to mint.
     * @param  index   The index to update to.
     */
    function mint(address account, uint256 amount, uint128 index) external;

    /**
     * @notice Burns tokens of msg.sender.
     * @dev    MUST only be callable by the SpokePortal.
     * @param  amount  The amount of M Token to burn.
     */
    function burn(uint256 amount) external;

    /**
     * @notice Updates the latest index and latest accrual time in storage.
     * @param  index The new index to compute present amounts from principal amounts.
     */
    function updateIndex(uint128 index) external;
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title  IRegistrarLike interface
 * @author M^0 Labs
 * @notice Subset of Registrar interface required for Portal contracts.
 */
interface IRegistrarLike {
    /**
     * @notice Adds `account` to `list`.
     * @param  list    The key for some list.
     * @param  account The address of some account to be added.
     */
    function addToList(bytes32 list, address account) external;

    /**
     * @notice Removes `account` from `list`.
     * @param  list    The key for some list.
     * @param  account The address of some account to be removed.
     */
    function removeFromList(bytes32 list, address account) external;

    /**
     * @notice Sets `key` to `value`.
     * @param  key   Some key.
     * @param  value Some value.
     */
    function setKey(bytes32 key, bytes32 value) external;

    /**
     * @notice Returns the value of `key`.
     * @param  key Some key.
     * @return Some value.
     */
    function get(bytes32 key) external view returns (bytes32);

    /**
     * @notice Returns whether `list` contains `account`.
     * @param  list    The key for some list.
     * @param  account The address of some account.
     * @return Whether `list` contains `account`.
     */
    function listContains(bytes32 list, address account) external view returns (bool);

    /// @notice Returns the address of the Portal contract.
    function portal() external view returns (address);
}

File 4 of 26 : ISpokePortal.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { IPortal } from "./IPortal.sol";

/**
 * @title  SpokePortal interface.
 * @author M^0 Labs
 */
interface ISpokePortal is IPortal {
    ///////////////////////////////////////////////////////////////////////////
    //                                 EVENTS                                //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Emitted when M Token index is received from Mainnet.
     * @param  index M token index.
     */
    event MTokenIndexReceived(uint128 index);

    /**
     * @notice Emitted when the Registrar key is received from Mainnet.
     * @param  key   The Registrar key of some value.
     * @param  value The value.
     */
    event RegistrarKeyReceived(bytes32 indexed key, bytes32 value);

    /**
     * @notice Emitted when the Registrar list status is received from Mainnet.
     * @param  listName The name of the list.
     * @param  account  The account.
     * @param  status   Indicates if the account is added or removed from the list.
     */
    event RegistrarListStatusReceived(bytes32 indexed listName, address indexed account, bool status);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { PayloadType } from "../libs/PayloadEncoder.sol";

/**
 * @title  IPortal interface
 * @author M^0 Labs
 * @notice Subset of functions inherited by both IHubPortal and ISpokePortal.
 */
interface IPortal {
    ///////////////////////////////////////////////////////////////////////////
    //                                 EVENTS                                //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Emitted when M token is sent to a destination chain.
     * @param  sourceToken        The address of the token on the source chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  destinationToken   The address of the token on the destination chain.
     * @param  sender             The address that bridged the M tokens via the Portal.
     * @param  recipient          The account receiving tokens on destination chain.
     * @param  amount             The amount of tokens.
     * @param  index              The M token index.
     * @param  messageId          The unique identifier for the sent message.
     */
    event MTokenSent(
        address indexed sourceToken,
        uint256 destinationChainId,
        address destinationToken,
        address indexed sender,
        address indexed recipient,
        uint256 amount,
        uint128 index,
        bytes32 messageId
    );

    /**
     * @notice Emitted when M token is received from a source chain.
     * @param  sourceChainId    The EVM chain Id of the source chain.
     * @param  destinationToken The address of the token on the destination chain.
     * @param  sender           The account sending tokens.
     * @param  recipient        The account receiving tokens.
     * @param  amount           The amount of tokens.
     * @param  index            The M token index
     */
    event MTokenReceived(
        uint256 sourceChainId,
        address indexed destinationToken,
        address indexed sender,
        address indexed recipient,
        uint256 amount,
        uint128 index
    );

    /**
     * @notice Emitted when wrapping M token is failed on the destination.
     * @param  destinationWrappedToken The address of the Wrapped M Token on the destination chain.
     * @param  recipient               The account receiving tokens.
     * @param  amount                  The amount of tokens.
     */
    event WrapFailed(address indexed destinationWrappedToken, address indexed recipient, uint256 amount);

    /**
     * @notice Emitted when the Bridge contract responsible for cross-chain communication is set
     * @param  previousBridge The address of the previous Bridge.
     * @param  newBridge      The address of the new Bridge.
     */
    event BridgeSet(address indexed previousBridge, address indexed newBridge);

    /**
     * @notice Emitted when M token is set for the remote chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  mToken             The address of M token on the destination chain.
     */
    event DestinationMTokenSet(uint256 indexed destinationChainId, address mToken);

    /**
     * @notice Emitted when a bridging path support status is updated.
     * @param  sourceToken        The address of the token on the current chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  destinationToken   The address of the token on the destination chain.
     * @param  supported          `True` if the token is supported, `false` otherwise.
     */
    event SupportedBridgingPathSet(
        address indexed sourceToken, uint256 indexed destinationChainId, address indexed destinationToken, bool supported
    );

    /**
     * @notice Emitted when the gas limit for a payload type is updated.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  payloadType        The type of payload.
     * @param  gasLimit           The gas limit.
     */
    event PayloadGasLimitSet(uint256 indexed destinationChainId, PayloadType indexed payloadType, uint256 gasLimit);

    ///////////////////////////////////////////////////////////////////////////
    //                             CUSTOM ERRORS                             //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice Thrown when the M token is 0x0.
    error ZeroMToken();

    /// @notice Thrown when the M token is 0x0.
    error ZeroRemoteMToken();

    /// @notice Thrown when the Registrar address is 0x0.
    error ZeroRegistrar();

    /// @notice Thrown when the Swap Facility address is 0x0.
    error ZeroSwapFacility();

    /// @notice Thrown when the Bridge address is 0x0.
    error ZeroBridge();

    /// @notice Thrown when the source token address is 0x0.
    error ZeroSourceToken();

    /// @notice Thrown when the destination token address is 0x0.
    error ZeroDestinationToken();

    /// @notice Thrown when the transfer amount is 0.
    error ZeroAmount();

    /// @notice Thrown when the refund address is 0x0.
    error ZeroRefundAddress();

    /// @notice Thrown when the recipient address is 0x0.
    error ZeroRecipient();

    /// @notice Thrown when `receiveMessage` function caller is not the bridge.
    error NotBridge();

    /// @notice Thrown in `transferMLikeToken` function when bridging path is not supported
    error UnsupportedBridgingPath(address sourceToken, uint256 destinationChainId, address destinationToken);

    /// @notice Thrown when the destination chain id is equal to the source one.
    error InvalidDestinationChain(uint256 destinationChainId);

    ///////////////////////////////////////////////////////////////////////////
    //                          VIEW/PURE FUNCTIONS                          //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice The current index of the Portal's earning mechanism.
    function currentIndex() external view returns (uint128);

    /// @notice The address of the M token.
    function mToken() external view returns (address);

    /// @notice The address of the Registrar contract.
    function registrar() external view returns (address);

    /// @notice The address of the Bridge contract responsible for cross-chain communication.
    function bridge() external view returns (address);

    /// @notice The address of the Swap Facility contract.
    function swapFacility() external view returns (address);

    /// @notice The address of the original caller of `transfer` and `transferMLikeToken` functions.
    function msgSender() external view returns (address);

    /**
     * @notice Returns the address of M token on the destination chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @return mToken             The address of M token on the destination chain.
     */
    function destinationMToken(uint256 destinationChainId) external view returns (address mToken);

    /**
     * @notice Indicates whether the provided bridging path is supported.
     * @param  sourceToken        The address of the token on the current chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  destinationToken   The address of the token on the destination chain.
     * @return supported          `True` if the token is supported, `false` otherwise.
     */
    function supportedBridgingPath(
        address sourceToken,
        uint256 destinationChainId,
        address destinationToken
    ) external view returns (bool supported);

    /**
     * @notice Returns the gas limit required to process a message
     *         with the specified payload type on the destination chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  payloadType        The type of payload.
     * @return gasLimit           The gas limit.
     */
    function payloadGasLimit(uint256 destinationChainId, PayloadType payloadType) external view returns (uint256 gasLimit);

    /**
     * @notice Returns the delivery fee for token transfer.
     * @dev    The fee must be passed as mgs.value when calling `transfer` or `transferMLikeToken`.
     * @param  amount             The amount of tokens to transfer.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  recipient          The account to receive tokens.
     * @param  fee                The delivery fee.
     */
    function quoteTransfer(uint256 amount, uint256 destinationChainId, address recipient) external view returns (uint256 fee);

    ///////////////////////////////////////////////////////////////////////////
    //                         INTERACTIVE FUNCTIONS                         //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Initializes the Proxy's storage
     * @param  bridge_        The address of the Bridge contract.
     * @param  initialOwner_  The address of the owner.
     * @param  initialPauser_ The address of the pauser.
     */
    function initialize(address bridge_, address initialOwner_, address initialPauser_) external;

    /**
     * @notice Sets address of the Bridge contract responsible for cross-chain communication.
     * @param  bridge The address of the Bridge.
     */
    function setBridge(address bridge) external;

    /**
     * @notice Sets M token address on the remote chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  mToken             The address of M token on the destination chain.
     */
    function setDestinationMToken(uint256 destinationChainId, address mToken) external;

    /**
     * @notice Sets a bridging path support status.
     * @param  sourceToken        The address of the token on the current chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  destinationToken   The address of the token on the destination chain.
     * @param  supported          `True` if the token is supported, `false` otherwise.
     */
    function setSupportedBridgingPath(
        address sourceToken,
        uint256 destinationChainId,
        address destinationToken,
        bool supported
    ) external;

    /**
     * @notice Sets the gas limit required to process a message
     *         with the specified payload type on the destination chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  payloadType        The payload type.
     * @param  gasLimit           The gas limit required to process the message.
     */
    function setPayloadGasLimit(uint256 destinationChainId, PayloadType payloadType, uint256 gasLimit) external;

    /**
     * @notice Transfers M token to the destination chain.
     * @param  amount             The amount of tokens to transfer.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  recipient          The account to receive tokens.
     * @param  refundAddress      The address to receive excess native gas on the source chain.
     * @return messageId          The unique identifier of the message sent.
     */
    function transfer(
        uint256 amount,
        uint256 destinationChainId,
        address recipient,
        address refundAddress
    ) external payable returns (bytes32 messageId);

    /**
     * @notice Transfers M or Wrapped M Token to the destination chain.
     * @dev    If wrapping on the destination fails, the recipient will receive $M token.
     * @param  amount             The amount of tokens to transfer.
     * @param  sourceToken        The address of the token (M or Wrapped M) on the source chain.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  destinationToken   The address of the token (M or Wrapped M) on the destination chain.
     * @param  recipient          The account to receive tokens.
     * @param  refundAddress      The address to receive excess native gas on the source chain.
     * @return messageId          The unique identifier of the message sent.
     */
    function transferMLikeToken(
        uint256 amount,
        address sourceToken,
        uint256 destinationChainId,
        address destinationToken,
        address recipient,
        address refundAddress
    ) external payable returns (bytes32 messageId);

    /**
     * @notice Receives a message from the bridge.
     * @param  sourceChainId The EVM chain Id of the source chain.
     * @param  payload       The message payload.
     */
    function receiveMessage(uint256 sourceChainId, bytes calldata payload) external;
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol";
import { Migratable } from "../lib/common/src/Migratable.sol";
import { IndexingMath } from "../lib/common/src/libs/IndexingMath.sol";

import { IPortal } from "./interfaces/IPortal.sol";
import { IBridge } from "./interfaces/IBridge.sol";
import { ISwapFacilityLike } from "./interfaces/ISwapFacilityLike.sol";
import { PausableOwnableUpgradeable } from "./access/PausableOwnableUpgradeable.sol";
import { TypeConverter } from "./libs/TypeConverter.sol";
import { SafeCall } from "./libs/SafeCall.sol";
import { PayloadType, PayloadEncoder } from "./libs/PayloadEncoder.sol";
import { ReentrancyLock } from "./libs/ReentrancyLock.sol";

/**
 * @title  Portal
 * @author M^0 Labs
 * @notice Base Portal contract inherited by HubPortal and SpokePortal.
 */
abstract contract Portal is IPortal, PausableOwnableUpgradeable, ReentrancyLock, Migratable {
    using TypeConverter for *;
    using PayloadEncoder for bytes;
    using SafeCall for address;

    /// @inheritdoc IPortal
    address public immutable mToken;

    /// @inheritdoc IPortal
    address public immutable registrar;

    /// @inheritdoc IPortal
    address public immutable swapFacility;

    /// @inheritdoc IPortal
    address public bridge;

    /// @inheritdoc IPortal
    mapping(address sourceToken => mapping(uint256 destinationChainId => mapping(address destinationToken => bool supported)))
        public supportedBridgingPath;

    /// @inheritdoc IPortal
    mapping(uint256 destinationChainId => address mToken) public destinationMToken;

    /// @inheritdoc IPortal
    mapping(uint256 destinationChainId => mapping(PayloadType payloadType => uint256 gasLimit)) public payloadGasLimit;

    /**
     * @notice Constructs the Implementation contract
     * @dev    Sets immutable storage.
     * @param  mToken_    The address of M token.
     * @param  registrar_ The address of Registrar.
     * @param  swapFacility_ The address of Swap Facility.
     */
    constructor(address mToken_, address registrar_, address swapFacility_) {
        _disableInitializers();

        if ((mToken = mToken_) == address(0)) revert ZeroMToken();
        if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
        if ((swapFacility = swapFacility_) == address(0)) revert ZeroSwapFacility();
    }

    /**
     * @notice Initializes the Proxy's storage
     * @param  bridge_        The address of M token.
     * @param  initialOwner_  The address of the owner.
     * @param  initialPauser_ The address of the pauser.
     */
    function _initialize(address bridge_, address initialOwner_, address initialPauser_) internal onlyInitializing {
        if ((bridge = bridge_) == address(0)) revert ZeroBridge();
        __PausableOwnable_init(initialOwner_, initialPauser_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                     EXTERNAL VIEW/PURE FUNCTIONS                      //
    ///////////////////////////////////////////////////////////////////////////

    /// @inheritdoc IPortal
    function currentIndex() external view returns (uint128) {
        return _currentIndex();
    }

    /// @inheritdoc IPortal
    function quoteTransfer(
        uint256 amount_,
        uint256 destinationChainId_,
        address recipient_
    ) external view returns (uint256 fee_) {
        // NOTE: for quoting delivery only the payload size and destination chain matter.
        address destinationToken_ = destinationMToken[destinationChainId_];
        bytes memory payload_ =
            PayloadEncoder.encodeTokenTransfer(amount_, destinationToken_, msg.sender, recipient_, _currentIndex());
        return IBridge(bridge).quote(destinationChainId_, payloadGasLimit[destinationChainId_][PayloadType.Token], payload_);
    }

    /// @inheritdoc IPortal
    function msgSender() public view returns (address) {
        return _getLocker();
    }

    ///////////////////////////////////////////////////////////////////////////
    //                     EXTERNAL INTERACTIVE FUNCTIONS                    //
    ///////////////////////////////////////////////////////////////////////////

    /// @inheritdoc IPortal
    function transfer(
        uint256 amount_,
        uint256 destinationChainId_,
        address recipient_,
        address refundAddress_
    ) external payable whenNotPaused isNotLocked returns (bytes32 messageId_) {
        return _transferMLikeToken(
            amount_, mToken, destinationChainId_, destinationMToken[destinationChainId_], recipient_, refundAddress_
        );
    }

    /// @inheritdoc IPortal
    function transferMLikeToken(
        uint256 amount_,
        address sourceToken_,
        uint256 destinationChainId_,
        address destinationToken_,
        address recipient_,
        address refundAddress_
    ) external payable whenNotPaused isNotLocked returns (bytes32 messageId_) {
        if (!supportedBridgingPath[sourceToken_][destinationChainId_][destinationToken_]) {
            revert UnsupportedBridgingPath(sourceToken_, destinationChainId_, destinationToken_);
        }

        return _transferMLikeToken(amount_, sourceToken_, destinationChainId_, destinationToken_, recipient_, refundAddress_);
    }

    /// @inheritdoc IPortal
    function receiveMessage(uint256 sourceChainId_, bytes calldata payload_) external {
        if (msg.sender != bridge) revert NotBridge();

        PayloadType payloadType_ = payload_.getPayloadType();

        if (payloadType_ == PayloadType.Token) {
            _receiveMLikeToken(sourceChainId_, payload_);
            return;
        }

        _receiveCustomPayload(payloadType_, payload_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                     OWNER INTERACTIVE FUNCTIONS                       //
    ///////////////////////////////////////////////////////////////////////////

    /// @inheritdoc IPortal
    function setBridge(address newBridge_) external onlyOwner {
        if (newBridge_ == address(0)) revert ZeroBridge();
        address previousBridge_ = bridge;

        bridge = newBridge_;
        emit BridgeSet(previousBridge_, newBridge_);
    }

    /// @inheritdoc IPortal
    function setDestinationMToken(uint256 destinationChainId_, address mToken_) external onlyOwner {
        if (destinationChainId_ == block.chainid) revert InvalidDestinationChain(destinationChainId_);
        if (mToken_ == address(0)) revert ZeroMToken();

        destinationMToken[destinationChainId_] = mToken_;
        emit DestinationMTokenSet(destinationChainId_, mToken_);
    }

    /// @inheritdoc IPortal
    function setSupportedBridgingPath(
        address sourceToken_,
        uint256 destinationChainId_,
        address destinationToken_,
        bool supported_
    ) external onlyOwner {
        if (sourceToken_ == address(0)) revert ZeroSourceToken();
        if (destinationChainId_ == block.chainid) revert InvalidDestinationChain(destinationChainId_);
        if (destinationToken_ == address(0)) revert ZeroDestinationToken();

        supportedBridgingPath[sourceToken_][destinationChainId_][destinationToken_] = supported_;
        emit SupportedBridgingPathSet(sourceToken_, destinationChainId_, destinationToken_, supported_);
    }

    /// @inheritdoc IPortal
    function setPayloadGasLimit(uint256 destinationChainId_, PayloadType payloadType_, uint256 gasLimit_) external onlyOwner {
        payloadGasLimit[destinationChainId_][payloadType_] = gasLimit_;
        emit PayloadGasLimitSet(destinationChainId_, payloadType_, gasLimit_);
    }

    /**
     * @dev   Performs the contract migration by delegate-calling `migrator_`.
     * @param migrator_ The address of a migrator contract.
     */
    function migrate(address migrator_) external onlyOwner {
        _migrate(migrator_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                INTERNAL/PRIVATE INTERACTIVE FUNCTIONS                 //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @dev Transfers M or Wrapped M token to the remote chain.
     * @param  amount_             The amount of tokens to transfer.
     * @param  sourceToken_        The address of the source token.
     * @param  destinationChainId_ The EVM chain Id of the destination chain.
     * @param  destinationToken_   The address of the destination token.
     * @param  recipient_          The address of the recipient.
     * @param  refundAddress_      The address to receive the fee refund.
     * @return messageId_          The ID uniquely identifying the message.
     */
    function _transferMLikeToken(
        uint256 amount_,
        address sourceToken_,
        uint256 destinationChainId_,
        address destinationToken_,
        address recipient_,
        address refundAddress_
    ) private returns (bytes32 messageId_) {
        _revertIfZeroAmount(amount_);
        _revertIfZeroRefundAddress(refundAddress_);

        if (destinationToken_ == address(0)) revert ZeroDestinationToken();
        if (recipient_ == address(0)) revert ZeroRecipient();

        IERC20 mToken_ = IERC20(mToken);
        uint256 startingBalance_ = mToken_.balanceOf(address(this));

        // transfer source token from the sender
        IERC20(sourceToken_).transferFrom(msg.sender, address(this), amount_);

        // if the source token isn't M token, unwrap it
        if (sourceToken_ != address(mToken_)) {
            IERC20(sourceToken_).approve(swapFacility, amount_);
            ISwapFacilityLike(swapFacility).swapOutM(sourceToken_, amount_, address(this));
        }

        // The actual amount of M tokens that Portal received from the sender.
        // Accounts for potential rounding errors when transferring between earners and non-earners,
        // as well as potential fee-on-transfer functionality in the source token.
        uint256 actualAmount_ = mToken_.balanceOf(address(this)) - startingBalance_;

        if (amount_ > actualAmount_) {
            unchecked {
                // If the difference between the specified transfer amount and the actual amount exceeds
                // the maximum acceptable rounding error (e.g., due to fee-on-transfer in an extension token)
                // transfer the actual amount, not the specified.

                // Otherwise, the specified amount will be transferred and the deficit caused by rounding error will
                // be covered from the yield earned by HubPortal.
                if (amount_ - actualAmount_ > _getMaxRoundingError()) {
                    amount_ = actualAmount_;
                    // Ensure that updated transfer amount is greater than 0
                    _revertIfZeroAmount(amount_);
                }
            }
        }

        // Burn M tokens on Spoke.
        // In case of Hub, only update the bridged principal amount as tokens already transferred.
        _burnOrLock(destinationChainId_, amount_);

        uint128 index_ = _currentIndex();
        bytes memory payload_ = PayloadEncoder.encodeTokenTransfer(amount_, destinationToken_, msg.sender, recipient_, index_);
        messageId_ = _sendMessage(destinationChainId_, PayloadType.Token, refundAddress_, payload_);

        // Prevent stack too deep
        uint256 transferAmount_ = amount_;

        emit MTokenSent(
            sourceToken_, destinationChainId_, destinationToken_, msg.sender, recipient_, transferAmount_, index_, messageId_
        );
    }

    /**
     * @dev   Sends a cross-chain message using the bridge.
     * @param destinationChainId_ The EVM chain Id of the destination chain.
     * @param payloadType_        The type of the payload.
     * @param refundAddress_      The address to receive the fee refund.
     * @param payload_            The message payload to send.
     * @return messageId_         The ID uniquely identifying the message.
     */
    function _sendMessage(
        uint256 destinationChainId_,
        PayloadType payloadType_,
        address refundAddress_,
        bytes memory payload_
    ) internal returns (bytes32 messageId_) {
        return IBridge(bridge).sendMessage{ value: msg.value }(
            destinationChainId_, payloadGasLimit[destinationChainId_][payloadType_], refundAddress_, payload_
        );
    }

    /**
     * @dev   Handles token transfer message on the destination.
     * @param sourceChainId_ The EVM chain Id of the source chain.
     * @param payload_       The message payload.
     */
    function _receiveMLikeToken(uint256 sourceChainId_, bytes memory payload_) private {
        (uint256 amount_, address destinationToken_, address sender_, address recipient_, uint128 index_) =
            payload_.decodeTokenTransfer();

        emit MTokenReceived(sourceChainId_, destinationToken_, sender_, recipient_, amount_, index_);

        address mToken_ = mToken;
        if (destinationToken_ == mToken_) {
            // mints or unlocks M Token to the recipient
            _mintOrUnlock(sourceChainId_, recipient_, amount_, index_);
        } else {
            // mints or unlocks M Token to the Portal
            _mintOrUnlock(sourceChainId_, address(this), amount_, index_);

            // wraps M token and transfers it to the recipient
            _wrap(mToken_, destinationToken_, recipient_, amount_);
        }
    }

    /**
     * @dev   Wraps $M token to the token specified by `destinationWrappedToken_`.
     *        If wrapping fails transfers $M token to `recipient_`.
     * @param mToken_                  The address of $M token.
     * @param destinationWrappedToken_ The address of the wrapped token.
     * @param recipient_               The account to receive wrapped token.
     * @param amount_                  The amount to wrap.
     */
    function _wrap(address mToken_, address destinationWrappedToken_, address recipient_, uint256 amount_) private {
        IERC20(mToken_).approve(swapFacility, amount_);

        // Attempt to wrap $M token
        // NOTE: the call might fail with out-of-gas exception
        //       even if the destination token is the valid wrapped M token.
        //       Recipients must support both $M and wrapped $M transfers.
        (bool success,) =
            swapFacility.call(abi.encodeCall(ISwapFacilityLike.swapInM, (destinationWrappedToken_, amount_, recipient_)));

        if (!success) {
            emit WrapFailed(destinationWrappedToken_, recipient_, amount_);
            // Reset approval to prevent a potential double-spend attack
            IERC20(mToken_).approve(swapFacility, 0);
            // Transfer $M token to the recipient
            IERC20(mToken_).transfer(recipient_, amount_);
        }
    }

    /**
     * @dev   Overridden in SpokePortal to handle custom payload messages.
     * @param payloadType_  The type of the payload (Index, Key, or List).
     * @param payload_      The message payload to process.
     */
    function _receiveCustomPayload(PayloadType payloadType_, bytes memory payload_) internal virtual { }

    /**
     * @dev   HubPortal:   unlocks and transfers `amount_` M tokens to `recipient_`.
     *        SpokePortal: mints `amount_` M tokens to `recipient_`.
     * @param sourceChainId_ The EVM id of the source chain.
     * @param recipient_     The account receiving M tokens.
     * @param amount_        The amount of M tokens to unlock/mint.
     * @param index_         The index from the source chain.
     */
    function _mintOrUnlock(uint256 sourceChainId_, address recipient_, uint256 amount_, uint128 index_) internal virtual { }

    /**
     * @dev   HubPortal:   locks amount_` M tokens.
     *        SpokePortal: burns `amount_` M tokens.
     * @param destinationChainId_ The EVM id of the destination chain.
     * @param amount_             The amount of M tokens to lock/burn.
     */
    function _burnOrLock(uint256 destinationChainId_, uint256 amount_) internal virtual { }

    ///////////////////////////////////////////////////////////////////////////
    //                 INTERNAL/PRIVATE VIEW/PURE FUNCTIONS                  //
    ///////////////////////////////////////////////////////////////////////////

    /// @dev Reverts if `amount` is zero.
    function _revertIfZeroAmount(uint256 amount_) private pure {
        if (amount_ == 0) revert ZeroAmount();
    }

    /// @dev Reverts if `refundAddress` is zero address.
    function _revertIfZeroRefundAddress(address refundAddress_) internal pure {
        if (refundAddress_ == address(0)) revert ZeroRefundAddress();
    }

    /// @inheritdoc Migratable
    function _getMigrator() internal pure override returns (address migrator_) {
        // NOTE: in this version only the owner-controlled migration via `migrate()` function is supported
        return address(0);
    }

    /// @dev Returns the current M token index used by the Portal.
    function _currentIndex() internal view virtual returns (uint128) { }

    /// @dev Returns the maximum rounding error that can occur when transferring M tokens to the Portal
    function _getMaxRoundingError() private view returns (uint256) {
        return _currentIndex() / IndexingMath.EXP_SCALED_ONE + 1;
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { BytesParser } from "./BytesParser.sol";
import { TypeConverter } from "./TypeConverter.sol";

enum PayloadType {
    Token,
    Index,
    Key,
    List
}

/**
 * @title  PayloadEncoder
 * @author M^0 Labs
 * @notice Encodes and decodes cross-chain message payloads.
 */
library PayloadEncoder {
    using BytesParser for bytes;
    using TypeConverter for *;

    uint256 internal constant PAYLOAD_TYPE_LENGTH = 1;

    /// @dev PayloadType.Token = 0, PayloadType.Index = 1, PayloadType.Key = 2, PayloadType.List = 3
    uint256 internal constant MAX_PAYLOAD_TYPE = 3;

    error InvalidPayloadLength(uint256 length);
    error InvalidPayloadType(uint8 value);

    /**
     * @notice Decodes the payload type from the payload.
     * @param payload_      The payload to decode.
     * @return payloadType_ The decoded payload type.
     */
    function getPayloadType(bytes memory payload_) internal pure returns (PayloadType payloadType_) {
        if (payload_.length < PAYLOAD_TYPE_LENGTH) revert InvalidPayloadLength(payload_.length);

        uint8 type_;
        (type_,) = payload_.asUint8Unchecked(0);

        if (type_ > MAX_PAYLOAD_TYPE) revert InvalidPayloadType(type_);
        payloadType_ = PayloadType(type_);
    }

    /**
     * @notice Encodes a token transfer payload.
     * @dev    Encoded values are packed using `abi.encodePacked`.
     * @param amount_           The amount of tokens to transfer.
     * @param destinationToken_ The address of the destination token.
     * @param sender_           The address of the sender.
     * @param recipient_        The address of the recipient.
     * @param index_            The M token index.
     * @return encoded_         The encoded payload.
     */
    function encodeTokenTransfer(
        uint256 amount_,
        address destinationToken_,
        address sender_,
        address recipient_,
        uint128 index_
    ) internal pure returns (bytes memory encoded_) {
        encoded_ = abi.encodePacked(PayloadType.Token, amount_, destinationToken_, sender_, recipient_, index_);
    }

    /**
     * @notice Decodes a token transfer payload.
     * @param payload_           The payload to decode.
     * @return amount_           The amount of tokens to transfer.
     * @return destinationToken_ The address of the destination token.
     * @return sender_           The address of the sender.
     * @return recipient_        The address of the recipient.
     * @return index_            The M token index.
     */
    function decodeTokenTransfer(bytes memory payload_)
        internal
        pure
        returns (uint256 amount_, address destinationToken_, address sender_, address recipient_, uint128 index_)
    {
        uint256 offset_ = PAYLOAD_TYPE_LENGTH;

        (amount_, offset_) = payload_.asUint256Unchecked(offset_);
        (destinationToken_, offset_) = payload_.asAddressUnchecked(offset_);
        (sender_, offset_) = payload_.asAddressUnchecked(offset_);
        (recipient_, offset_) = payload_.asAddressUnchecked(offset_);
        (index_, offset_) = payload_.asUint128Unchecked(offset_);

        payload_.checkLength(offset_);
    }

    /**
     * @notice Encodes M token index payload.
     * @param index_    The M token index.
     * @return encoded_ The encoded payload.
     */
    function encodeIndex(uint128 index_) internal pure returns (bytes memory encoded_) {
        encoded_ = abi.encodePacked(PayloadType.Index, index_);
    }

    /**
     * @notice Decodes M token index payload.
     * @param payload_ The payload to decode.
     * @return index_  The M token index.
     */
    function decodeIndex(bytes memory payload_) internal pure returns (uint128 index_) {
        uint256 offset_ = PAYLOAD_TYPE_LENGTH;

        (index_, offset_) = payload_.asUint128Unchecked(offset_);

        payload_.checkLength(offset_);
    }

    /**
     * @notice Encodes a Registrar key-value pair payload.
     * @param key_      The key.
     * @param value_    The value.
     * @return encoded_ The encoded payload.
     */
    function encodeKey(bytes32 key_, bytes32 value_) internal pure returns (bytes memory encoded_) {
        encoded_ = abi.encodePacked(PayloadType.Key, key_, value_);
    }

    /**
     * @notice Decodes a Registrar key-value pair payload.
     * @param payload_ The payload to decode.
     * @return key_    The key.
     * @return value_  The value.
     */
    function decodeKey(bytes memory payload_) internal pure returns (bytes32 key_, bytes32 value_) {
        uint256 offset_ = PAYLOAD_TYPE_LENGTH;

        (key_, offset_) = payload_.asBytes32Unchecked(offset_);
        (value_, offset_) = payload_.asBytes32Unchecked(offset_);

        payload_.checkLength(offset_);
    }

    /**
     * @notice Encodes a list update payload.
     * @param listName_ The name of the list.
     * @param account_  The address of the account.
     * @param add_      Indicates whether to add or remove the account from the list.
     * @return encoded_ The encoded payload.
     */
    function encodeListUpdate(bytes32 listName_, address account_, bool add_) internal pure returns (bytes memory encoded_) {
        encoded_ = abi.encodePacked(PayloadType.List, listName_, account_, add_);
    }

    /**
     * @notice Decodes a list update payload.
     * @param payload_   The payload to decode.
     * @return listName_ The name of the list.
     * @return account_  The address of the account.
     * @return add_      Indicates whether the account was added or removed from the list.
     */
    function decodeListUpdate(bytes memory payload_) internal pure returns (bytes32 listName_, address account_, bool add_) {
        uint256 offset_ = PAYLOAD_TYPE_LENGTH;

        (listName_, offset_) = payload_.asBytes32Unchecked(offset_);
        (account_, offset_) = payload_.asAddressUnchecked(offset_);
        (add_, offset_) = payload_.asBoolUnchecked(offset_);

        payload_.checkLength(offset_);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title IMTokenLike interface
 * @author M^0 Labs
 * @notice  Subset of M Token interface required for Portal contracts.
 */
interface IMTokenLike {
    /**
     * @notice Emitted when there is insufficient balance to decrement from `account`.
     * @param  account     The account with insufficient balance.
     * @param  rawBalance  The raw balance of the account.
     * @param  amount      The amount to decrement the `rawBalance` by.
     */
    error InsufficientBalance(address account, uint256 rawBalance, uint256 amount);

    /// @notice The current index that would be written to storage if `updateIndex` is called.
    function currentIndex() external view returns (uint128);

    /**
     * @notice Checks if account is an earner.
     * @param  account The account to check.
     * @return True if account is an earner, false otherwise.
     */
    function isEarning(address account) external view returns (bool);

    /// @notice Starts earning for caller if allowed by TTG.
    function startEarning() external;

    /// @notice Stops earning for the account.
    function stopEarning(address account) external;
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  ERC20 Token Standard.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-20: https://eips.ethereum.org/EIPS/eip-20
 */
interface IERC20 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `spender` has been approved for `amount` of the token balance of `account`.
     * @param  account The address of the account.
     * @param  spender The address of the spender being approved for the allowance.
     * @param  amount  The amount of the allowance being approved.
     */
    event Approval(address indexed account, address indexed spender, uint256 amount);

    /**
     * @notice Emitted when `amount` tokens is transferred from `sender` to `recipient`.
     * @param  sender    The address of the sender who's token balance is decremented.
     * @param  recipient The address of the recipient who's token balance is incremented.
     * @param  amount    The amount of tokens being transferred.
     */
    event Transfer(address indexed sender, address indexed recipient, uint256 amount);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows a calling account to approve `spender` to spend up to `amount` of its token balance.
     * @dev    MUST emit an `Approval` event.
     * @param  spender The address of the account being allowed to spend up to the allowed amount.
     * @param  amount  The amount of the allowance being approved.
     * @return Whether or not the approval was successful.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @notice Allows a calling account to transfer `amount` tokens to `recipient`.
     * @param  recipient The address of the recipient who's token balance will be incremented.
     * @param  amount    The amount of tokens being transferred.
     * @return Whether or not the transfer was successful.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @notice Allows a calling account to transfer `amount` tokens from `sender`, with allowance, to a `recipient`.
     * @param  sender    The address of the sender who's token balance will be decremented.
     * @param  recipient The address of the recipient who's token balance will be incremented.
     * @param  amount    The amount of tokens being transferred.
     * @return Whether or not the transfer was successful.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the allowance `spender` is allowed to spend on behalf of `account`.
     * @param  account The address of the account who's token balance `spender` is allowed to spend.
     * @param  spender The address of an account allowed to spend on behalf of `account`.
     * @return The amount `spender` can spend on behalf of `account`.
     */
    function allowance(address account, address spender) external view returns (uint256);

    /**
     * @notice Returns the token balance of `account`.
     * @param  account The address of some account.
     * @return The token balance of `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the number of decimals UIs should assume all amounts have.
    function decimals() external view returns (uint8);

    /// @notice Returns the name of the contract/token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the current total supply of the token.
    function totalSupply() external view returns (uint256);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

import { IMigratable } from "./interfaces/IMigratable.sol";

/**
 * @title  Abstract implementation for exposing the ability to migrate a contract, extending ERC-1967.
 * @author M^0 Labs
 */
abstract contract Migratable is IMigratable {
    /* ============ Variables ============ */

    /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`.
    uint256 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /* ============ Interactive Functions ============ */

    /// @inheritdoc IMigratable
    function migrate() external {
        _migrate(_getMigrator());
    }

    /* ============ View/Pure Functions ============ */

    /// @inheritdoc IMigratable
    function implementation() public view returns (address implementation_) {
        assembly {
            implementation_ := sload(_IMPLEMENTATION_SLOT)
        }
    }

    /* ============ Internal Interactive Functions ============ */

    /**
     * @dev   Performs an arbitrary migration by delegate-calling `migrator_`.
     * @param migrator_ The address of a migrator contract.
     */
    function _migrate(address migrator_) internal {
        if (migrator_ == address(0)) revert ZeroMigrator();

        if (migrator_.code.length == 0) revert InvalidMigrator();

        address oldImplementation_ = implementation();

        (bool success_, ) = migrator_.delegatecall("");
        if (!success_) revert MigrationFailed();

        address newImplementation_ = implementation();

        emit Migrated(migrator_, oldImplementation_, newImplementation_);

        // NOTE: Redundant event emitted to conform to the EIP-1967 standard.
        emit Upgraded(newImplementation_);
    }

    /* ============ Internal View/Pure Functions ============ */

    /// @dev Returns the address of a migrator contract.
    function _getMigrator() internal view virtual returns (address);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

import { UIntMath } from "./UIntMath.sol";

/**
 * @title  Helper library for indexing math functions.
 * @author M^0 Labs
 */
library IndexingMath {
    /* ============ Variables ============ */

    /// @notice The scaling of indexes for exponent math.
    uint56 internal constant EXP_SCALED_ONE = 1e12;

    /* ============ Custom Errors ============ */

    /// @notice Emitted when a division by zero occurs.
    error DivisionByZero();

    /* ============ Exposed Functions ============ */

    /**
     * @notice Helper function to calculate `(x * EXP_SCALED_ONE) / y`, rounded down.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function divide240By128Down(uint240 x, uint128 y) internal pure returns (uint112) {
        if (y == 0) revert DivisionByZero();

        unchecked {
            // NOTE: While `uint256(x) * EXP_SCALED_ONE` can technically overflow, these divide/multiply functions are
            //       only used for the purpose of principal/present amount calculations for continuous indexing, and
            //       so for an `x` to be large enough to overflow this, it would have to be a possible result of
            //       `multiply112By128Down` or `multiply112By128Up`, which would already satisfy
            //       `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
            return UIntMath.safe112((uint256(x) * EXP_SCALED_ONE) / y);
        }
    }

    /**
     * @notice Helper function to calculate `(x * EXP_SCALED_ONE) / y`, rounded up.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function divide240By128Up(uint240 x, uint128 y) internal pure returns (uint112) {
        if (y == 0) revert DivisionByZero();

        unchecked {
            // NOTE: While `uint256(x) * EXP_SCALED_ONE` can technically overflow, these divide/multiply functions are
            //       only used for the purpose of principal/present amount calculations for continuous indexing, and
            //       so for an `x` to be large enough to overflow this, it would have to be a possible result of
            //       `multiply112By128Down` or `multiply112By128Up`, which would already satisfy
            //       `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
            return UIntMath.safe112(((uint256(x) * EXP_SCALED_ONE) + y - 1) / y);
        }
    }

    /**
     * @notice Helper function to calculate `(x * y) / EXP_SCALED_ONE`, rounded down.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function multiply112By128Down(uint112 x, uint128 y) internal pure returns (uint240) {
        unchecked {
            return uint240((uint256(x) * y) / EXP_SCALED_ONE);
        }
    }

    /**
     * @notice Helper function to calculate `(x * index) / EXP_SCALED_ONE`, rounded up.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function multiply112By128Up(uint112 x, uint128 index) internal pure returns (uint240 z) {
        unchecked {
            return uint240(((uint256(x) * index) + (EXP_SCALED_ONE - 1)) / EXP_SCALED_ONE);
        }
    }

    /**
     * @dev    Returns the present amount (rounded down) given the principal amount and an index.
     * @param  principalAmount The principal amount.
     * @param  index           An index.
     * @return The present amount rounded down.
     */
    function getPresentAmountRoundedDown(uint112 principalAmount, uint128 index) internal pure returns (uint240) {
        return multiply112By128Down(principalAmount, index);
    }

    /**
     * @dev    Returns the present amount (rounded up) given the principal amount and an index.
     * @param  principalAmount The principal amount.
     * @param  index           An index.
     * @return The present amount rounded up.
     */
    function getPresentAmountRoundedUp(uint112 principalAmount, uint128 index) internal pure returns (uint240) {
        return multiply112By128Up(principalAmount, index);
    }

    /**
     * @dev    Returns the principal amount given the present amount, using the current index.
     * @param  presentAmount The present amount.
     * @param  index         An index.
     * @return The principal amount rounded down.
     */
    function getPrincipalAmountRoundedDown(uint240 presentAmount, uint128 index) internal pure returns (uint112) {
        return divide240By128Down(presentAmount, index);
    }

    /**
     * @dev    Returns the principal amount given the present amount, using the current index.
     * @param  presentAmount The present amount.
     * @param  index         An index.
     * @return The principal amount rounded up.
     */
    function getPrincipalAmountRoundedUp(uint240 presentAmount, uint128 index) internal pure returns (uint112) {
        return divide240By128Up(presentAmount, index);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

interface IBridge {
    ///////////////////////////////////////////////////////////////////////////
    //                             CUSTOM ERRORS                             //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice Thrown when `sendMessage` function caller is not the portal.
    error NotPortal();

    /// @notice Thrown when the portal address is 0x0.
    error ZeroPortal();

    ///////////////////////////////////////////////////////////////////////////
    //                          VIEW/PURE FUNCTIONS                          //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice Returns the address of the portal.
    function portal() external view returns (address);

    /**
     * @notice Returns the fee for sending a message to the remote chain
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  gasLimit           The gas limit to execute the message on the destination chain.
     * @param  payload            The message payload to send.
     * @return fee                The fee for sending a message.
     */
    function quote(uint256 destinationChainId, uint256 gasLimit, bytes memory payload) external view returns (uint256 fee);

    ///////////////////////////////////////////////////////////////////////////
    //                         INTERACTIVE FUNCTIONS                         //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Sends a message to the remote chain.
     * @dev    Only EVM chains are supported.
     * @param  destinationChainId The EVM chain Id of the destination chain.
     * @param  gasLimit           The gas limit to execute the message on the destination chain.
     * @param  refundAddress      The address to refund the fee to.
     * @param  payload            The message payload to send.
     * @return messageId          The unique identifier of the message sent.
     */
    function sendMessage(
        uint256 destinationChainId,
        uint256 gasLimit,
        address refundAddress,
        bytes memory payload
    ) external payable returns (bytes32 messageId);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title  Subset of SwapFacility interface.
 * @author M0 Labs
 */
interface ISwapFacilityLike {
    /**
     * @notice Swaps $M token to $M Extension.
     * @param  extensionOut The address of the M Extension to swap to.
     * @param  amount       The amount of $M token to swap.
     * @param  recipient    The address to receive the swapped $M Extension tokens.
     */
    function swapInM(address extensionOut, uint256 amount, address recipient) external;

    /**
     * @notice Swaps $M Extension to $M token.
     * @param  extensionIn The address of the $M Extension to swap from.
     * @param  amount      The amount of $M Extension tokens to swap.
     * @param  recipient   The address to receive $M tokens.
     */
    function swapOutM(address extensionIn, uint256 amount, address recipient) external;
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

import { OwnableUpgradeable } from "../../lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "../../lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol";
import { IPausableOwnable } from "../interfaces/IPausableOwnable.sol";

abstract contract PausableOwnableUpgradeable is OwnableUpgradeable, PausableUpgradeable, IPausableOwnable {
    /// @custom:storage-location erc7201:m0.storage.PausableOwnable
    struct PausableOwnableStorage {
        address _pauser;
    }

    /// @dev keccak256(abi.encode(uint256(keccak256("m0.storage.PausableOwnable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _PAUSER_SLOT = 0x9ab2df69adadda616016eab34dbdcdbe8b11549e0ceb446652474b2cb1ced800;

    /// @dev Modifier to allow only the pauser and the owner to access pausing functionality
    modifier onlyOwnerOrPauser() {
        if (pauser() != _msgSender() && owner() != _msgSender()) revert Unauthorized(_msgSender());
        _;
    }

    ///////////////////////////////////////////////////////////////////////////
    //                       EXTERNAL VIEW FUNCTIONS                         //
    ///////////////////////////////////////////////////////////////////////////

    /// @dev Returns the address of the current pauser.
    function pauser() public view virtual returns (address) {
        PausableOwnableStorage storage $ = _getPausableOwnableStorage();
        return $._pauser;
    }

    ///////////////////////////////////////////////////////////////////////////
    //                     EXTERNAL INTERACTIVE FUNCTIONS                    //
    ///////////////////////////////////////////////////////////////////////////

    /// @inheritdoc IPausableOwnable
    function transferPauserRole(address newPauser_) external onlyOwner {
        if (newPauser_ == address(0)) revert ZeroPauser();
        _transferPauserRole(newPauser_);
    }

    /// @inheritdoc IPausableOwnable
    function pause() external onlyOwnerOrPauser {
        _pause();
    }

    /// @inheritdoc IPausableOwnable
    function unpause() external onlyOwnerOrPauser {
        _unpause();
    }

    ///////////////////////////////////////////////////////////////////////////
    //                        PRIVATE PURE FUNCTIONS                         //
    ///////////////////////////////////////////////////////////////////////////

    function _getPausableOwnableStorage() private pure returns (PausableOwnableStorage storage $) {
        assembly {
            $.slot := _PAUSER_SLOT
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    //                              INITIALIZERS                             //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @dev Initializes itself and the parent contracts.
     * @param initialOwner_  The address of initial owner.
     * @param initialPauser_ The address of initial pauser.
     */
    function __PausableOwnable_init(address initialOwner_, address initialPauser_) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner_);
        __Pausable_init_unchained();
        __PausableOwnable_init_unchained(initialPauser_);
    }

    /**
     * @dev Initializes the contract.
     * @param initialPauser_ The address of initial pauser.
     */
    function __PausableOwnable_init_unchained(address initialPauser_) internal onlyInitializing {
        if (initialPauser_ == address(0)) revert ZeroPauser();
        _transferPauserRole(initialPauser_);
    }

    ///////////////////////////////////////////////////////////////////////////
    //                     INTERNAL INTERACTIVE FUNCTIONS                    //
    ///////////////////////////////////////////////////////////////////////////

    function _transferPauserRole(address newPauser_) internal {
        PausableOwnableStorage storage $ = _getPausableOwnableStorage();
        address previousPauser_ = $._pauser;
        $._pauser = newPauser_;
        emit PauserTransferred(previousPauser_, newPauser_);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

library TypeConverter {
    function toBytes32(address address_) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(address_)));
    }

    function toAddress(bytes32 addressBytes32_) internal pure returns (address) {
        return address(uint160(uint256(addressBytes32_)));
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

library SafeCall {
    function safeCall(address target, bytes memory data) internal returns (bool success) {
        if (target.code.length > 0) {
            (success,) = target.call(data);
        }
    }
}

File 17 of 26 : ReentrancyLock.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { Locker } from "./Locker.sol";

/**
 * @author Uniswap Labs.
 *         Copied from https://github.com/Uniswap/v4-periphery/blob/main/src/base/ReentrancyLock.sol.
 * @dev    Use Uniswap version of the contract when Linea supports transient storage.
 */
contract ReentrancyLock {
    error ContractLocked();

    modifier isNotLocked() {
        if (Locker.get() != address(0)) revert ContractLocked();
        Locker.set(msg.sender);
        _;
        Locker.set(address(0));
    }

    function _getLocker() internal view returns (address) {
        return Locker.get();
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title  BytesParser
 * @author Wormhole Labs
 * @notice Parses tightly packed data.
 * @dev    Modified from
 *         https://github.com/wormhole-foundation/wormhole-solidity-sdk/blob/main/src/libraries/BytesParsing.sol
 */
library BytesParser {
    error LengthMismatch(uint256 encodedLength, uint256 expectedLength);
    error InvalidBool(uint8 value);

    function checkLength(bytes memory encoded_, uint256 expected_) internal pure {
        if (encoded_.length != expected_) revert LengthMismatch(encoded_.length, expected_);
    }

    function asUint8Unchecked(bytes memory encoded_, uint256 offset_) internal pure returns (uint8 value_, uint256 nextOffset_) {
        assembly ("memory-safe") {
            nextOffset_ := add(offset_, 1)
            value_ := mload(add(encoded_, nextOffset_))
        }
    }

    function asBoolUnchecked(bytes memory encoded_, uint256 offset_) internal pure returns (bool value_, uint256 nextOffset_) {
        uint8 uint8Value_;
        (uint8Value_, nextOffset_) = asUint8Unchecked(encoded_, offset_);

        if (uint8Value_ & 0xfe != 0) revert InvalidBool(uint8Value_);

        uint256 cleanedValue_ = uint256(uint8Value_);
        // skip 2x iszero opcode
        assembly ("memory-safe") {
            value_ := cleanedValue_
        }
    }

    function asUint256Unchecked(
        bytes memory encoded_,
        uint256 offset_
    ) internal pure returns (uint256 value_, uint256 nextOffset_) {
        assembly ("memory-safe") {
            nextOffset_ := add(offset_, 32)
            value_ := mload(add(encoded_, nextOffset_))
        }
    }

    function asBytes32Unchecked(
        bytes memory encoded_,
        uint256 offset_
    ) internal pure returns (bytes32 value_, uint256 nextOffset_) {
        uint256 uint256Value_;
        (uint256Value_, nextOffset_) = asUint256Unchecked(encoded_, offset_);
        value_ = bytes32(uint256Value_);
    }

    function asUint128Unchecked(
        bytes memory encoded_,
        uint256 offset_
    ) internal pure returns (uint128 value_, uint256 nextOffset_) {
        assembly ("memory-safe") {
            nextOffset_ := add(offset_, 16)
            value_ := mload(add(encoded_, nextOffset_))
        }
    }

    function asAddressUnchecked(
        bytes memory encoded_,
        uint256 offset_
    ) internal pure returns (address value_, uint256 nextOffset_) {
        assembly ("memory-safe") {
            nextOffset_ := add(offset_, 20)
            value_ := mload(add(encoded_, nextOffset_))
        }
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  Interface for exposing the ability to migrate a contract, extending the ERC-1967 interface.
 * @author M^0 Labs
 */
interface IMigratable {
    /* ============ Events ============ */

    /**
     * @notice Emitted when a migration to a new implementation is performed.
     * @param  migrator          The address that performed the migration.
     * @param  oldImplementation The address of the old implementation.
     * @param  newImplementation The address of the new implementation.
     */
    event Migrated(address indexed migrator, address indexed oldImplementation, address indexed newImplementation);

    /**
     * @notice Emitted when the implementation address for the proxy is changed.
     * @param  implementation The address of the new implementation for the proxy.
     */
    event Upgraded(address indexed implementation);

    /// @notice Emitted when calling `stopEarning` for an account approved as earner by the Registrar.
    error InvalidMigrator();

    /// @notice Emitted when the delegatecall to a migrator fails.
    error MigrationFailed();

    /// @notice Emitted when the zero address is passed as a migrator.
    error ZeroMigrator();

    /* ============ Interactive Functions ============ */

    /// @notice Performs an arbitrarily defined migration.
    function migrate() external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the address of the current implementation contract.
    function implementation() external view returns (address);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  Library to perform safe math operations on uint types
 * @author M^0 Labs
 */
library UIntMath {
    /* ============ Custom Errors ============ */

    /// @notice Emitted when a passed value is greater than the maximum value of uint16.
    error InvalidUInt16();

    /// @notice Emitted when a passed value is greater than the maximum value of uint40.
    error InvalidUInt40();

    /// @notice Emitted when a passed value is greater than the maximum value of uint48.
    error InvalidUInt48();

    /// @notice Emitted when a passed value is greater than the maximum value of uint112.
    error InvalidUInt112();

    /// @notice Emitted when a passed value is greater than the maximum value of uint128.
    error InvalidUInt128();

    /// @notice Emitted when a passed value is greater than the maximum value of uint240.
    error InvalidUInt240();

    /* ============ Internal View/Pure Functions ============ */

    /**
     * @notice Casts a uint256 value to a uint16, ensuring that it is less than or equal to the maximum uint16 value.
     * @param  n The value to cast.
     * @return The value casted to uint16.
     */
    function safe16(uint256 n) internal pure returns (uint16) {
        if (n > type(uint16).max) revert InvalidUInt16();
        return uint16(n);
    }

    /**
     * @notice Casts a uint256 value to a uint40, ensuring that it is less than or equal to the maximum uint40 value.
     * @param  n The value to cast.
     * @return The value casted to uint40.
     */
    function safe40(uint256 n) internal pure returns (uint40) {
        if (n > type(uint40).max) revert InvalidUInt40();
        return uint40(n);
    }

    /**
     * @notice Casts a uint256 value to a uint48, ensuring that it is less than or equal to the maximum uint48 value.
     * @param  n The value to cast.
     * @return The value casted to uint48.
     */
    function safe48(uint256 n) internal pure returns (uint48) {
        if (n > type(uint48).max) revert InvalidUInt48();
        return uint48(n);
    }

    /**
     * @notice Casts a uint256 value to a uint112, ensuring that it is less than or equal to the maximum uint112 value.
     * @param  n The value to cast.
     * @return The value casted to uint112.
     */
    function safe112(uint256 n) internal pure returns (uint112) {
        if (n > type(uint112).max) revert InvalidUInt112();
        return uint112(n);
    }

    /**
     * @notice Casts a uint256 value to a uint128, ensuring that it is less than or equal to the maximum uint128 value.
     * @param  n The value to cast.
     * @return The value casted to uint128.
     */
    function safe128(uint256 n) internal pure returns (uint128) {
        if (n > type(uint128).max) revert InvalidUInt128();
        return uint128(n);
    }

    /**
     * @notice Casts a uint256 value to a uint240, ensuring that it is less than or equal to the maximum uint240 value.
     * @param  n The value to cast.
     * @return The value casted to uint240.
     */
    function safe240(uint256 n) internal pure returns (uint240) {
        if (n > type(uint240).max) revert InvalidUInt240();
        return uint240(n);
    }

    /**
     * @notice Limits a uint256 value to the maximum uint32 value.
     * @param  n The value to bound.
     * @return The value limited to within uint32 bounds.
     */
    function bound32(uint256 n) internal pure returns (uint32) {
        return uint32(min256(n, uint256(type(uint32).max)));
    }

    /**
     * @notice Limits a uint256 value to the maximum uint112 value.
     * @param  n The value to bound.
     * @return The value limited to within uint112 bounds.
     */
    function bound112(uint256 n) internal pure returns (uint112) {
        return uint112(min256(n, uint256(type(uint112).max)));
    }

    /**
     * @notice Limits a uint256 value to the maximum uint128 value.
     * @param  n The value to bound.
     * @return The value limited to within uint128 bounds.
     */
    function bound128(uint256 n) internal pure returns (uint128) {
        return uint128(min256(n, uint256(type(uint128).max)));
    }

    /**
     * @notice Limits a uint256 value to the maximum uint240 value.
     * @param  n The value to bound.
     * @return The value limited to within uint240 bounds.
     */
    function bound240(uint256 n) internal pure returns (uint240) {
        return uint240(min256(n, uint256(type(uint240).max)));
    }

    /**
     * @notice Compares two uint32 values and returns the larger one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The larger value.
     */
    function max32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a > b ? a : b;
    }

    /**
     * @notice Compares two uint40 values and returns the larger one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The larger value.
     */
    function max40(uint40 a, uint40 b) internal pure returns (uint40) {
        return a > b ? a : b;
    }

    /**
     * @notice Compares two uint128 values and returns the larger one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The larger value.
     */
    function max128(uint128 a, uint128 b) internal pure returns (uint128) {
        return a > b ? a : b;
    }

    /**
     * @notice Compares two uint240 values and returns the larger one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The larger value.
     */
    function max240(uint240 a, uint240 b) internal pure returns (uint240) {
        return a > b ? a : b;
    }

    /**
     * @notice Compares two uint32 values and returns the lesser one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The lesser value.
     */
    function min32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a < b ? a : b;
    }

    /**
     * @notice Compares two uint40 values and returns the lesser one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The lesser value.
     */
    function min40(uint40 a, uint40 b) internal pure returns (uint40) {
        return a < b ? a : b;
    }

    /**
     * @notice Compares two uint240 values and returns the lesser one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The lesser value.
     */
    function min240(uint240 a, uint240 b) internal pure returns (uint240) {
        return a < b ? a : b;
    }

    /**
     * @notice Compares two uint112 values and returns the lesser one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The lesser value.
     */
    function min112(uint112 a, uint112 b) internal pure returns (uint112) {
        return a < b ? a : b;
    }

    /**
     * @notice Compares two uint256 values and returns the lesser one.
     * @param  a Value to compare.
     * @param  b Value to compare.
     * @return The lesser value.
     */
    function min256(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../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.
 *
 * 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
    struct OwnableStorage {
        address _owner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

    /**
     * @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.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        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) {
        OwnableStorage storage $ = _getOwnableStorage();
        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 {
        OwnableStorage storage $ = _getOwnableStorage();
        address oldOwner = $._owner;
        $._owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../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 {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

    /**
     * @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);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @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();
        _;
    }

    function __Pausable_init() internal onlyInitializing {
    }

    function __Pausable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        PausableStorage storage $ = _getPausableStorage();
        return $._paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

interface IPausableOwnable {
    ///////////////////////////////////////////////////////////////////////////
    //                                 EVENTS                                //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Emitted when the pauser role is transferred.
     * @param previousPauser The previous pauser.
     * @param newPauser      The new pauser.
     */
    event PauserTransferred(address indexed previousPauser, address indexed newPauser);

    ///////////////////////////////////////////////////////////////////////////
    //                             CUSTOM ERRORS                             //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice Thrown when the pauser address is 0x0.
    error ZeroPauser();

    /**
     * @notice Thrown when the caller account is not authorized to perform an operation.
     * @param  account The caller.
     */
    error Unauthorized(address account);

    ///////////////////////////////////////////////////////////////////////////
    //                          VIEW/PURE FUNCTIONS                          //
    ///////////////////////////////////////////////////////////////////////////

    /// @notice Returns the address of the pauser.
    function pauser() external view returns (address);

    /// @notice Returns `true` if the contract is paused, and `false` otherwise.
    //function paused() external view returns (bool);

    ///////////////////////////////////////////////////////////////////////////
    //                         INTERACTIVE FUNCTIONS                         //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @notice Transfers the pauser role to new address
     * @param newPauser Address of the new pauser
     */
    function transferPauserRole(address newPauser) external;

    /**
     * @notice Triggers the paused state.
     * @dev    The contract must not be paused.
     */
    function pause() external;

    /**
     * @notice Returns to normal state.
     * @dev    The contract must be paused.
     */
    function unpause() external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/**
    * @author Uniswap Labs.
    *         Adapted from https://github.com/Uniswap/v4-periphery/blob/main/src/libraries/Locker.sol for Linea deployment.
    * @dev    Use Uniswap version of the contract when Linea supports transient storage.
 */
library Locker {
    // The slot holding the locker state. bytes32(uint256(keccak256("LockedBy")) - 1)
    bytes32 constant LOCKED_BY_SLOT = 0x0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a;

    function set(address locker) internal {
        assembly {
            sstore(LOCKED_BY_SLOT, locker)
        }
    }

    function get() internal view returns (address locker) {
        assembly {
            locker := sload(LOCKED_BY_SLOT)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}

Settings
{
  "remappings": [
    "@ensdomains/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/=lib/uniswap-v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "@uniswap/v4-core/=lib/uniswap-v4-periphery/lib/v4-core/",
    "common/=lib/common/src/",
    "ds-test/=lib/safe-utils/lib/solidity-stringutils/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/uniswap-v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin/",
    "permit2/=lib/uniswap-v4-periphery/lib/permit2/",
    "protocol/=lib/protocol/",
    "safe-smart-account/=lib/safe-utils/lib/safe-smart-account/",
    "safe-utils/=lib/safe-utils/src/",
    "solidity-http/=lib/safe-utils/lib/solidity-http/src/",
    "solidity-stringutils/=lib/safe-utils/lib/solidity-stringutils/",
    "solmate/=lib/uniswap-v4-periphery/lib/v4-core/lib/solmate/",
    "ttg/=lib/ttg/",
    "uniswap-v4-periphery/=lib/uniswap-v4-periphery/",
    "v4-core/=lib/uniswap-v4-periphery/lib/v4-core/src/",
    "wrapped-m-token/=lib/wrapped-m-token/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"mToken_","type":"address"},{"internalType":"address","name":"registrar_","type":"address"},{"internalType":"address","name":"swapFacility_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"uint8","name":"value","type":"uint8"}],"name":"InvalidBool","type":"error"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"}],"name":"InvalidDestinationChain","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidMigrator","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"InvalidPayloadLength","type":"error"},{"inputs":[{"internalType":"uint8","name":"value","type":"uint8"}],"name":"InvalidPayloadType","type":"error"},{"inputs":[{"internalType":"uint256","name":"encodedLength","type":"uint256"},{"internalType":"uint256","name":"expectedLength","type":"uint256"}],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"MigrationFailed","type":"error"},{"inputs":[],"name":"NotBridge","type":"error"},{"inputs":[],"name":"NotInitializing","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":"account","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"sourceToken","type":"address"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"destinationToken","type":"address"}],"name":"UnsupportedBridgingPath","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroBridge","type":"error"},{"inputs":[],"name":"ZeroDestinationToken","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroMigrator","type":"error"},{"inputs":[],"name":"ZeroPauser","type":"error"},{"inputs":[],"name":"ZeroRecipient","type":"error"},{"inputs":[],"name":"ZeroRefundAddress","type":"error"},{"inputs":[],"name":"ZeroRegistrar","type":"error"},{"inputs":[],"name":"ZeroRemoteMToken","type":"error"},{"inputs":[],"name":"ZeroSourceToken","type":"error"},{"inputs":[],"name":"ZeroSwapFacility","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousBridge","type":"address"},{"indexed":true,"internalType":"address","name":"newBridge","type":"address"}],"name":"BridgeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"mToken","type":"address"}],"name":"DestinationMTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"MTokenIndexReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sourceChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"destinationToken","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"MTokenReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sourceToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"destinationToken","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"messageId","type":"bytes32"}],"name":"MTokenSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"migrator","type":"address"},{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousPauser","type":"address"},{"indexed":true,"internalType":"address","name":"newPauser","type":"address"}],"name":"PauserTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"enum PayloadType","name":"payloadType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"PayloadGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"RegistrarKeyReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"listName","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"RegistrarListStatusReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sourceToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"destinationToken","type":"address"},{"indexed":false,"internalType":"bool","name":"supported","type":"bool"}],"name":"SupportedBridgingPathSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"destinationWrappedToken","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WrapFailed","type":"event"},{"inputs":[],"name":"bridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"}],"name":"destinationMToken","outputs":[{"internalType":"address","name":"mToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"bridge_","type":"address"},{"internalType":"address","name":"initialOwner_","type":"address"},{"internalType":"address","name":"initialPauser_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_","type":"address"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"enum PayloadType","name":"payloadType","type":"uint8"}],"name":"payloadGasLimit","outputs":[{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"address","name":"recipient_","type":"address"}],"name":"quoteTransfer","outputs":[{"internalType":"uint256","name":"fee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sourceChainId_","type":"uint256"},{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newBridge_","type":"address"}],"name":"setBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"address","name":"mToken_","type":"address"}],"name":"setDestinationMToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"enum PayloadType","name":"payloadType_","type":"uint8"},{"internalType":"uint256","name":"gasLimit_","type":"uint256"}],"name":"setPayloadGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sourceToken_","type":"address"},{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"address","name":"destinationToken_","type":"address"},{"internalType":"bool","name":"supported_","type":"bool"}],"name":"setSupportedBridgingPath","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sourceToken","type":"address"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"destinationToken","type":"address"}],"name":"supportedBridgingPath","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapFacility","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"address","name":"refundAddress_","type":"address"}],"name":"transfer","outputs":[{"internalType":"bytes32","name":"messageId_","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"sourceToken_","type":"address"},{"internalType":"uint256","name":"destinationChainId_","type":"uint256"},{"internalType":"address","name":"destinationToken_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"address","name":"refundAddress_","type":"address"}],"name":"transferMLikeToken","outputs":[{"internalType":"bytes32","name":"messageId_","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPauser_","type":"address"}],"name":"transferPauserRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e060405234801561001057600080fd5b5060405161337b38038061337b83398101604081905261002f91610197565b82828261003a6100c9565b6001600160a01b03831660808190526100665760405163b01d5e2b60e01b815260040160405180910390fd5b6001600160a01b03821660a0819052610092576040516379a6314960e01b815260040160405180910390fd5b6001600160a01b03811660c08190526100be57604051636880ffc960e11b815260040160405180910390fd5b5050505050506101da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101195760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101785780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b80516001600160a01b038116811461019257600080fd5b919050565b6000806000606084860312156101ac57600080fd5b6101b58461017b565b92506101c36020850161017b565b91506101d16040850161017b565b90509250925092565b60805160a05160c0516131026102796000396000818161047901528181611621015281816116e30152818161207a0152818161211e015261228d015260008181610257015281816125460152818161264e01526126bc01526000818161050d015281816107de015281816112e9015281816114cc015281816118b701528181611f5001528181611ff10152818161246501526127d001526131026000f3fe6080604052600436106101c25760003560e01c80638dd14802116100f7578063c0c53b8b11610095578063d737d0c711610064578063d737d0c714610590578063e78cea92146105a5578063e80f94cf146105c5578063f2fde38b146105e557600080fd5b8063c0c53b8b146104db578063c3b6f939146104fb578063ce5494bb1461052f578063d18150611461054f57600080fd5b80639fd0506d116100d15780639fd0506d14610452578063ae06b7e414610467578063b3c2c2141461049b578063bad383a6146104bb57600080fd5b80638dd14802146103e75780638fd3ab80146104075780639ca029921461041c57600080fd5b8063515c494611610164578063715018a61161013e578063715018a6146103705780638456cb59146103855780638b2717181461039a5780638da5cb5b146103d257600080fd5b8063515c4946146102d95780635c60da1b146102f95780635c975abb1461032d57600080fd5b80632b20e397116101a05780632b20e3971461024557806337090dc7146102915780633e2786fc146102a45780633f4ba83a146102c457600080fd5b806304291b56146101c757806308d11ceb146101e957806326987b601461020f575b600080fd5b3480156101d357600080fd5b506101e76101e2366004612ac5565b610605565b005b6101fc6101f7366004612b5f565b61072b565b6040519081526020015b60405180910390f35b34801561021b57600080fd5b50610224610843565b6040516fffffffffffffffffffffffffffffffff9091168152602001610206565b34801561025157600080fd5b506102797f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610206565b6101fc61029f366004612ba5565b610852565b3480156102b057600080fd5b506101fc6102bf366004612c0b565b6109b7565b3480156102d057600080fd5b506101e7610a8c565b3480156102e557600080fd5b506101e76102f4366004612c4f565b610b0b565b34801561030557600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54610279565b34801561033957600080fd5b507fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff165b6040519015158152602001610206565b34801561037c57600080fd5b506101e7610bae565b34801561039157600080fd5b506101e7610bc0565b3480156103a657600080fd5b506101fc6103b5366004612c85565b600360209081526000928352604080842090915290825290205481565b3480156103de57600080fd5b50610279610c03565b3480156103f357600080fd5b506101e7610402366004612cb1565b610c38565b34801561041357600080fd5b506101e7610ce8565b34801561042857600080fd5b50610279610437366004612ccc565b6002602052600090815260409020546001600160a01b031681565b34801561045e57600080fd5b50610279610cf2565b34801561047357600080fd5b506102797f000000000000000000000000000000000000000000000000000000000000000081565b3480156104a757600080fd5b506101e76104b6366004612cf3565b610d1b565b3480156104c757600080fd5b506101e76104d6366004612cb1565b610e76565b3480156104e757600080fd5b506101e76104f6366004612d42565b610eca565b34801561050757600080fd5b506102797f000000000000000000000000000000000000000000000000000000000000000081565b34801561053b57600080fd5b506101e761054a366004612cb1565b61103a565b34801561055b57600080fd5b5061036061056a366004612d7c565b600160209081526000938452604080852082529284528284209052825290205460ff1681565b34801561059c57600080fd5b5061027961104b565b3480156105b157600080fd5b50600054610279906001600160a01b031681565b3480156105d157600080fd5b506101e76105e0366004612daf565b611055565b3480156105f157600080fd5b506101e7610600366004612cb1565b611155565b6000546001600160a01b03163314610649576040517f7fea9dc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061068a83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506111a992505050565b905060008160038111156106a0576106a0612dd2565b036106eb576106e58484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061125092505050565b50505050565b6106e58184848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061134492505050565b60006107356113ae565b600061075f7f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b6001600160a01b03161461079f576040517f6f5ffb7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c7337f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b6000848152600260205260409020546108109086907f00000000000000000000000000000000000000000000000000000000000000009087906001600160a01b0316878761140a565b905061083b60007f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b949350505050565b600061084d6118b3565b905090565b600061085c6113ae565b60006108867f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b6001600160a01b0316146108c6576040517f6f5ffb7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108ee337f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b6001600160a01b03808716600090815260016020908152604080832089845282528083209388168352929052205460ff16610974576040517ff05eff4d0000000000000000000000000000000000000000000000000000000081526001600160a01b03808816600483015260248201879052851660448201526064015b60405180910390fd5b61098287878787878761140a565b90506109ad60007f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b9695505050505050565b6000828152600260205260408120546001600160a01b0316816109e4868333876109df6118b3565b611937565b60008054878252600360209081526040808420848052909152918290205491517fa66f84a90000000000000000000000000000000000000000000000000000000081529293506001600160a01b03169163a66f84a991610a4b918991908690600401612e6f565b602060405180830381865afa158015610a68573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ad9190612e8e565b33610a95610cf2565b6001600160a01b031614158015610abc575033610ab0610c03565b6001600160a01b031614155b15610b0157335b6040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161096b565b610b0961196f565b565b610b136119ff565b80600360008581526020019081526020016000206000846003811115610b3b57610b3b612dd2565b6003811115610b4c57610b4c612dd2565b8152602081019190915260400160002055816003811115610b6f57610b6f612dd2565b837f1a5992a751b521ef246a1a59ba188f21a01ef4df77a46061eacc5604768157e283604051610ba191815260200190565b60405180910390a3505050565b610bb66119ff565b610b096000611a4a565b33610bc9610cf2565b6001600160a01b031614158015610bf0575033610be4610c03565b6001600160a01b031614155b15610bfb5733610ac3565b610b09611ad3565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b610c406119ff565b6001600160a01b038116610c80576040517f361106cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917fc0634c245ce7b7b309744c30c030a783629555dc9f0c2c4cb54bdc9d9812d6209190a35050565b610b096000611b4c565b6000807f9ab2df69adadda616016eab34dbdcdbe8b11549e0ceb446652474b2cb1ced800610c28565b610d236119ff565b6001600160a01b038416610d63576040517f2dc2f38200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b468303610d9f576040517f202cb5130000000000000000000000000000000000000000000000000000000081526004810184905260240161096b565b6001600160a01b038216610ddf576040517f7c37399000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03848116600081815260016020908152604080832088845282528083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182528692917fb30eec013773a40f5bfc408b77c3eb479af144a816a0250387480ba905a06f95910160405180910390a450505050565b610e7e6119ff565b6001600160a01b038116610ebe576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ec781611d36565b50565b6000610ed4611dbf565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610f015750825b905060008267ffffffffffffffff166001148015610f1e5750303b155b905081158015610f2c575080155b15610f63576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610fc45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610fcf888888611dea565b83156110305784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6110426119ff565b610ec781611b4c565b600061084d611e6d565b61105d6119ff565b468203611099576040517f202cb5130000000000000000000000000000000000000000000000000000000081526004810183905260240161096b565b6001600160a01b0381166110d9576040517fb01d5e2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526002602090815260409182902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038516908117909155915191825283917fe1f5ca8210e2b8950e4626ed9e65573e6a90daf4c4f5d098ad4b7665a08992cb910160405180910390a25050565b61115d6119ff565b6001600160a01b0381166111a0576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526000600482015260240161096b565b610ec781611a4a565b60006001825110156111ec5781516040517ff61df23200000000000000000000000000000000000000000000000000000000815260040161096b91815260200190565b6001820151600360ff82161115611234576040517f55791e9400000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161096b565b8060ff16600381111561124957611249612dd2565b9392505050565b600080600080600061126186611e97565b94509450945094509450816001600160a01b0316836001600160a01b0316856001600160a01b03167f4dc9c05fa45102b9417bba8bc740e033a518560a8a91b3c3059d6124b381f2d28a89866040516112df9392919092835260208301919091526fffffffffffffffffffffffffffffffff16604082015260600190565b60405180910390a47f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038082169086160361132c5761132788848885611ec6565b611030565b61133888308885611ec6565b6110308186858961204a565b600182600381111561135857611358612dd2565b0361136a576113668161239f565b5050565b600282600381111561137e5761137e612dd2565b0361138c57611366816124c6565b60038260038111156113a0576113a0612dd2565b0361136657611366816125af565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff1615610b09576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611415876126eb565b61141e82612725565b6001600160a01b03841661145e576040517f7c37399000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831661149e576040517fd27b444300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611520573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115449190612e8e565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018b90529091506001600160a01b038916906323b872dd906064016020604051808303816000875af11580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190612ea7565b50816001600160a01b0316886001600160a01b031614611740576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018b905289169063095ea7b3906044016020604051808303816000875af1158015611679573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169d9190612ea7565b506040517f9e5e91f40000000000000000000000000000000000000000000000000000000081526001600160a01b038981166004830152602482018b90523060448301527f00000000000000000000000000000000000000000000000000000000000000001690639e5e91f490606401600060405180830381600087803b15801561172757600080fd5b505af115801561173b573d6000803e3d6000fd5b505050505b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156117a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c69190612e8e565b6117d09190612ef3565b9050808a11156117f7576117e2612765565b818b0311156117f7578099506117f78a6126eb565b611801888b6127a1565b600061180b6118b3565b9050600061181c8c8a338b86611937565b905061182b8a60008984612807565b604080518c81526001600160a01b038c811660208301529181018f90526fffffffffffffffffffffffffffffffff85166060820152608081018390529197508d91818b16913391908f16907f4c812797ac478127450f6a846f8ad0ae1ad2f055778469bcccc65d1db7a062039060a00160405180910390a45050505050509695505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015611913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084d9190612f06565b60606000868686868660405160200161195596959493929190612f38565b604051602081830303815290604052905095945050505050565b6119776128cf565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b33611a08610c03565b6001600160a01b031614610b09576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240161096b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b611adb6113ae565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336119e1565b6001600160a01b038116611b8c576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611bd0576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611bfa7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611c46576040519150601f19603f3d011682016040523d82523d6000602084013e611c4b565b606091505b5050905080611c86576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611cb07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b7f9ab2df69adadda616016eab34dbdcdbe8b11549e0ceb446652474b2cb1ced80080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f51c4874e0f23f262e04a38c51751336dde72126d67f53eb672aaff02996b3ef690600090a3505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b611df261292a565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038516908117909155611e5e576040517f361106cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e688282612968565b505050565b600061084d7f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b602181015160358201516049830151605d840151606d8086015190611ebc878261298a565b5091939590929450565b611ece6118b3565b6fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161115611fb2576040517f499d10810000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490526fffffffffffffffffffffffffffffffff831660448301527f0000000000000000000000000000000000000000000000000000000000000000169063499d1081906064015b600060405180830381600087803b158015611f9557600080fd5b505af1158015611fa9573d6000803e3d6000fd5b505050506106e5565b6040517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044015b600060405180830381600087803b15801561203657600080fd5b505af1158015611030573d6000803e3d6000fd5b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820183905285169063095ea7b3906044016020604051808303816000875af11580156120d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f69190612ea7565b506040516001600160a01b0384811660248301526044820183905283811660648301526000917f000000000000000000000000000000000000000000000000000000000000000090911690608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe274ffb800000000000000000000000000000000000000000000000000000000179052516121c59190612ff3565b6000604051808303816000865af19150503d8060008114612202576040519150601f19603f3d011682016040523d82523d6000602084013e612207565b606091505b505090508061239857826001600160a01b0316846001600160a01b03167f5e484dc77b9161908da1d2f0da5131cdaabac752ae7f0dd633ec8905627b51898460405161225591815260200190565b60405180910390a36040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af11580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123099190612ea7565b506040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526024820184905286169063a9059cbb906044016020604051808303816000875af1158015612372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123969190612ea7565b505b5050505050565b60006123aa826129d1565b6040516fffffffffffffffffffffffffffffffff821681529091507f26eac4431e1be7751a56292f289f392a5dfffd40080f0980ae5305ece0b591f89060200160405180910390a16123fa6118b3565b6fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161115611366576040517ffc387d5a0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063fc387d5a906024015b600060405180830381600087803b1580156124b257600080fd5b505af1158015612396573d6000803e3d6000fd5b6000806124d2836129e8565b91509150817f5e43f70ccc1c30e0a7e05c19edac5a078eeb1b68861d07f720d35e9320cc3d098260405161250891815260200190565b60405180910390a26040517f07a0033000000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a0033090604401600060405180830381600087803b15801561259257600080fd5b505af11580156125a6573d6000803e3d6000fd5b50505050505050565b60008060006125bd84612a04565b925092509250816001600160a01b0316837fadc61adcfd899e8535ed93fa1cfde40cd26793bfdcc8787bab210378d8de9be083604051612601911515815260200190565b60405180910390a3801561267d576040517fb4d87a12000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0383811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063b4d87a1290604401611f7b565b6040517fd48d8423000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0383811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063d48d84239060440161201c565b80600003610ec7576040517f1f2a200500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116610ec7576040517f66928bea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600064e8d4a510006127756118b3565b61277f919061300f565b61278a906001613076565b6fffffffffffffffffffffffffffffffff16905090565b6040517f42966c68000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401612498565b6000805485825260036020819052604083206001600160a01b03909216916360c0c0ba913491899186908a9081111561284257612842612dd2565b600381111561285357612853612dd2565b81526020019081526020016000205487876040518663ffffffff1660e01b8152600401612883949392919061309e565b60206040518083038185885af11580156128a1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906128c69190612e8e565b95945050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16610b09576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612932612a34565b610b09576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61297061292a565b61297982612a53565b612981612a5b565b61136681612a63565b808251146113665781516040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101919091526024810182905260440161096b565b601181810151906129e2838261298a565b50919050565b6021810151604180830151906129fe848261298a565b50915091565b602181015160358083015190600090612a1d8582612a6b565b9092509050612a2c858261298a565b509193909250565b6000612a3e611dbf565b5468010000000000000000900460ff16919050565b61115d61292a565b610b0961292a565b610e7e61292a565b600181830181015160009183019060fe811615612ab9576040517f63e37c1800000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161096b565b60ff1694909350915050565b600080600060408486031215612ada57600080fd5b83359250602084013567ffffffffffffffff811115612af857600080fd5b8401601f81018613612b0957600080fd5b803567ffffffffffffffff811115612b2057600080fd5b866020828401011115612b3257600080fd5b939660209190910195509293505050565b80356001600160a01b0381168114612b5a57600080fd5b919050565b60008060008060808587031215612b7557600080fd5b8435935060208501359250612b8c60408601612b43565b9150612b9a60608601612b43565b905092959194509250565b60008060008060008060c08789031215612bbe57600080fd5b86359550612bce60208801612b43565b945060408701359350612be360608801612b43565b9250612bf160808801612b43565b9150612bff60a08801612b43565b90509295509295509295565b600080600060608486031215612c2057600080fd5b8335925060208401359150612c3760408501612b43565b90509250925092565b803560048110612b5a57600080fd5b600080600060608486031215612c6457600080fd5b83359250612c7460208501612c40565b929592945050506040919091013590565b60008060408385031215612c9857600080fd5b82359150612ca860208401612c40565b90509250929050565b600060208284031215612cc357600080fd5b61124982612b43565b600060208284031215612cde57600080fd5b5035919050565b8015158114610ec757600080fd5b60008060008060808587031215612d0957600080fd5b612d1285612b43565b935060208501359250612d2760408601612b43565b91506060850135612d3781612ce5565b939692955090935050565b600080600060608486031215612d5757600080fd5b612d6084612b43565b9250612d6e60208501612b43565b9150612c3760408501612b43565b600080600060608486031215612d9157600080fd5b612d9a84612b43565b925060208401359150612c3760408501612b43565b60008060408385031215612dc257600080fd5b82359150612ca860208401612b43565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60005b83811015612e1c578181015183820152602001612e04565b50506000910152565b60008151808452612e3d816020860160208601612e01565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b8381528260208201526060604082015260006128c66060830184612e25565b600060208284031215612ea057600080fd5b5051919050565b600060208284031215612eb957600080fd5b815161124981612ce5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611de457611de4612ec4565b600060208284031215612f1857600080fd5b81516fffffffffffffffffffffffffffffffff8116811461124957600080fd5b600060048810612f71577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b5060f89690961b86526001860194909452606092831b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602187015291831b8216603586015290911b16604983015260801b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016605d820152606d0190565b60008251613005818460208701612e01565b9190910192915050565b60006fffffffffffffffffffffffffffffffff831680613058577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b806fffffffffffffffffffffffffffffffff84160491505092915050565b6fffffffffffffffffffffffffffffffff8181168382160190811115611de457611de4612ec4565b8481528360208201526001600160a01b03831660408201526080606082015260006109ad6080830184612e2556fea2646970667358221220ec128f9cf1bec45afe6a7c1c8315eb30685d98113dfdcdaa462200fb8a039e8264736f6c634300081a0033000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278

Deployed Bytecode

0x6080604052600436106101c25760003560e01c80638dd14802116100f7578063c0c53b8b11610095578063d737d0c711610064578063d737d0c714610590578063e78cea92146105a5578063e80f94cf146105c5578063f2fde38b146105e557600080fd5b8063c0c53b8b146104db578063c3b6f939146104fb578063ce5494bb1461052f578063d18150611461054f57600080fd5b80639fd0506d116100d15780639fd0506d14610452578063ae06b7e414610467578063b3c2c2141461049b578063bad383a6146104bb57600080fd5b80638dd14802146103e75780638fd3ab80146104075780639ca029921461041c57600080fd5b8063515c494611610164578063715018a61161013e578063715018a6146103705780638456cb59146103855780638b2717181461039a5780638da5cb5b146103d257600080fd5b8063515c4946146102d95780635c60da1b146102f95780635c975abb1461032d57600080fd5b80632b20e397116101a05780632b20e3971461024557806337090dc7146102915780633e2786fc146102a45780633f4ba83a146102c457600080fd5b806304291b56146101c757806308d11ceb146101e957806326987b601461020f575b600080fd5b3480156101d357600080fd5b506101e76101e2366004612ac5565b610605565b005b6101fc6101f7366004612b5f565b61072b565b6040519081526020015b60405180910390f35b34801561021b57600080fd5b50610224610843565b6040516fffffffffffffffffffffffffffffffff9091168152602001610206565b34801561025157600080fd5b506102797f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b6040516001600160a01b039091168152602001610206565b6101fc61029f366004612ba5565b610852565b3480156102b057600080fd5b506101fc6102bf366004612c0b565b6109b7565b3480156102d057600080fd5b506101e7610a8c565b3480156102e557600080fd5b506101e76102f4366004612c4f565b610b0b565b34801561030557600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54610279565b34801561033957600080fd5b507fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff165b6040519015158152602001610206565b34801561037c57600080fd5b506101e7610bae565b34801561039157600080fd5b506101e7610bc0565b3480156103a657600080fd5b506101fc6103b5366004612c85565b600360209081526000928352604080842090915290825290205481565b3480156103de57600080fd5b50610279610c03565b3480156103f357600080fd5b506101e7610402366004612cb1565b610c38565b34801561041357600080fd5b506101e7610ce8565b34801561042857600080fd5b50610279610437366004612ccc565b6002602052600090815260409020546001600160a01b031681565b34801561045e57600080fd5b50610279610cf2565b34801561047357600080fd5b506102797f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f627881565b3480156104a757600080fd5b506101e76104b6366004612cf3565b610d1b565b3480156104c757600080fd5b506101e76104d6366004612cb1565b610e76565b3480156104e757600080fd5b506101e76104f6366004612d42565b610eca565b34801561050757600080fd5b506102797f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b81565b34801561053b57600080fd5b506101e761054a366004612cb1565b61103a565b34801561055b57600080fd5b5061036061056a366004612d7c565b600160209081526000938452604080852082529284528284209052825290205460ff1681565b34801561059c57600080fd5b5061027961104b565b3480156105b157600080fd5b50600054610279906001600160a01b031681565b3480156105d157600080fd5b506101e76105e0366004612daf565b611055565b3480156105f157600080fd5b506101e7610600366004612cb1565b611155565b6000546001600160a01b03163314610649576040517f7fea9dc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061068a83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506111a992505050565b905060008160038111156106a0576106a0612dd2565b036106eb576106e58484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061125092505050565b50505050565b6106e58184848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061134492505050565b60006107356113ae565b600061075f7f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b6001600160a01b03161461079f576040517f6f5ffb7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c7337f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b6000848152600260205260409020546108109086907f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b9087906001600160a01b0316878761140a565b905061083b60007f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b949350505050565b600061084d6118b3565b905090565b600061085c6113ae565b60006108867f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b6001600160a01b0316146108c6576040517f6f5ffb7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108ee337f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b6001600160a01b03808716600090815260016020908152604080832089845282528083209388168352929052205460ff16610974576040517ff05eff4d0000000000000000000000000000000000000000000000000000000081526001600160a01b03808816600483015260248201879052851660448201526064015b60405180910390fd5b61098287878787878761140a565b90506109ad60007f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a55565b9695505050505050565b6000828152600260205260408120546001600160a01b0316816109e4868333876109df6118b3565b611937565b60008054878252600360209081526040808420848052909152918290205491517fa66f84a90000000000000000000000000000000000000000000000000000000081529293506001600160a01b03169163a66f84a991610a4b918991908690600401612e6f565b602060405180830381865afa158015610a68573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ad9190612e8e565b33610a95610cf2565b6001600160a01b031614158015610abc575033610ab0610c03565b6001600160a01b031614155b15610b0157335b6040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161096b565b610b0961196f565b565b610b136119ff565b80600360008581526020019081526020016000206000846003811115610b3b57610b3b612dd2565b6003811115610b4c57610b4c612dd2565b8152602081019190915260400160002055816003811115610b6f57610b6f612dd2565b837f1a5992a751b521ef246a1a59ba188f21a01ef4df77a46061eacc5604768157e283604051610ba191815260200190565b60405180910390a3505050565b610bb66119ff565b610b096000611a4a565b33610bc9610cf2565b6001600160a01b031614158015610bf0575033610be4610c03565b6001600160a01b031614155b15610bfb5733610ac3565b610b09611ad3565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b610c406119ff565b6001600160a01b038116610c80576040517f361106cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917fc0634c245ce7b7b309744c30c030a783629555dc9f0c2c4cb54bdc9d9812d6209190a35050565b610b096000611b4c565b6000807f9ab2df69adadda616016eab34dbdcdbe8b11549e0ceb446652474b2cb1ced800610c28565b610d236119ff565b6001600160a01b038416610d63576040517f2dc2f38200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b468303610d9f576040517f202cb5130000000000000000000000000000000000000000000000000000000081526004810184905260240161096b565b6001600160a01b038216610ddf576040517f7c37399000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03848116600081815260016020908152604080832088845282528083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182528692917fb30eec013773a40f5bfc408b77c3eb479af144a816a0250387480ba905a06f95910160405180910390a450505050565b610e7e6119ff565b6001600160a01b038116610ebe576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ec781611d36565b50565b6000610ed4611dbf565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610f015750825b905060008267ffffffffffffffff166001148015610f1e5750303b155b905081158015610f2c575080155b15610f63576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610fc45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610fcf888888611dea565b83156110305784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6110426119ff565b610ec781611b4c565b600061084d611e6d565b61105d6119ff565b468203611099576040517f202cb5130000000000000000000000000000000000000000000000000000000081526004810183905260240161096b565b6001600160a01b0381166110d9576040517fb01d5e2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526002602090815260409182902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038516908117909155915191825283917fe1f5ca8210e2b8950e4626ed9e65573e6a90daf4c4f5d098ad4b7665a08992cb910160405180910390a25050565b61115d6119ff565b6001600160a01b0381166111a0576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526000600482015260240161096b565b610ec781611a4a565b60006001825110156111ec5781516040517ff61df23200000000000000000000000000000000000000000000000000000000815260040161096b91815260200190565b6001820151600360ff82161115611234576040517f55791e9400000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161096b565b8060ff16600381111561124957611249612dd2565b9392505050565b600080600080600061126186611e97565b94509450945094509450816001600160a01b0316836001600160a01b0316856001600160a01b03167f4dc9c05fa45102b9417bba8bc740e033a518560a8a91b3c3059d6124b381f2d28a89866040516112df9392919092835260208301919091526fffffffffffffffffffffffffffffffff16604082015260600190565b60405180910390a47f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b038082169086160361132c5761132788848885611ec6565b611030565b61133888308885611ec6565b6110308186858961204a565b600182600381111561135857611358612dd2565b0361136a576113668161239f565b5050565b600282600381111561137e5761137e612dd2565b0361138c57611366816124c6565b60038260038111156113a0576113a0612dd2565b0361136657611366816125af565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff1615610b09576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611415876126eb565b61141e82612725565b6001600160a01b03841661145e576040517f7c37399000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831661149e576040517fd27b444300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611520573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115449190612e8e565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018b90529091506001600160a01b038916906323b872dd906064016020604051808303816000875af11580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190612ea7565b50816001600160a01b0316886001600160a01b031614611740576040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f627881166004830152602482018b905289169063095ea7b3906044016020604051808303816000875af1158015611679573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169d9190612ea7565b506040517f9e5e91f40000000000000000000000000000000000000000000000000000000081526001600160a01b038981166004830152602482018b90523060448301527f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f62781690639e5e91f490606401600060405180830381600087803b15801561172757600080fd5b505af115801561173b573d6000803e3d6000fd5b505050505b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156117a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c69190612e8e565b6117d09190612ef3565b9050808a11156117f7576117e2612765565b818b0311156117f7578099506117f78a6126eb565b611801888b6127a1565b600061180b6118b3565b9050600061181c8c8a338b86611937565b905061182b8a60008984612807565b604080518c81526001600160a01b038c811660208301529181018f90526fffffffffffffffffffffffffffffffff85166060820152608081018390529197508d91818b16913391908f16907f4c812797ac478127450f6a846f8ad0ae1ad2f055778469bcccc65d1db7a062039060a00160405180910390a45050505050509695505050505050565b60007f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015611913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084d9190612f06565b60606000868686868660405160200161195596959493929190612f38565b604051602081830303815290604052905095945050505050565b6119776128cf565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b33611a08610c03565b6001600160a01b031614610b09576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240161096b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b611adb6113ae565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336119e1565b6001600160a01b038116611b8c576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611bd0576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611bfa7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611c46576040519150601f19603f3d011682016040523d82523d6000602084013e611c4b565b606091505b5050905080611c86576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611cb07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b7f9ab2df69adadda616016eab34dbdcdbe8b11549e0ceb446652474b2cb1ced80080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f51c4874e0f23f262e04a38c51751336dde72126d67f53eb672aaff02996b3ef690600090a3505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b611df261292a565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038516908117909155611e5e576040517f361106cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e688282612968565b505050565b600061084d7f0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a5490565b602181015160358201516049830151605d840151606d8086015190611ebc878261298a565b5091939590929450565b611ece6118b3565b6fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161115611fb2576040517f499d10810000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490526fffffffffffffffffffffffffffffffff831660448301527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b169063499d1081906064015b600060405180830381600087803b158015611f9557600080fd5b505af1158015611fa9573d6000803e3d6000fd5b505050506106e5565b6040517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b16906340c10f19906044015b600060405180830381600087803b15801561203657600080fd5b505af1158015611030573d6000803e3d6000fd5b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278811660048301526024820183905285169063095ea7b3906044016020604051808303816000875af11580156120d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f69190612ea7565b506040516001600160a01b0384811660248301526044820183905283811660648301526000917f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f627890911690608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe274ffb800000000000000000000000000000000000000000000000000000000179052516121c59190612ff3565b6000604051808303816000865af19150503d8060008114612202576040519150601f19603f3d011682016040523d82523d6000602084013e612207565b606091505b505090508061239857826001600160a01b0316846001600160a01b03167f5e484dc77b9161908da1d2f0da5131cdaabac752ae7f0dd633ec8905627b51898460405161225591815260200190565b60405180910390a36040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af11580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123099190612ea7565b506040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526024820184905286169063a9059cbb906044016020604051808303816000875af1158015612372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123969190612ea7565b505b5050505050565b60006123aa826129d1565b6040516fffffffffffffffffffffffffffffffff821681529091507f26eac4431e1be7751a56292f289f392a5dfffd40080f0980ae5305ece0b591f89060200160405180910390a16123fa6118b3565b6fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161115611366576040517ffc387d5a0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff821660048201527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03169063fc387d5a906024015b600060405180830381600087803b1580156124b257600080fd5b505af1158015612396573d6000803e3d6000fd5b6000806124d2836129e8565b91509150817f5e43f70ccc1c30e0a7e05c19edac5a078eeb1b68861d07f720d35e9320cc3d098260405161250891815260200190565b60405180910390a26040517f07a0033000000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c6001600160a01b0316906307a0033090604401600060405180830381600087803b15801561259257600080fd5b505af11580156125a6573d6000803e3d6000fd5b50505050505050565b60008060006125bd84612a04565b925092509250816001600160a01b0316837fadc61adcfd899e8535ed93fa1cfde40cd26793bfdcc8787bab210378d8de9be083604051612601911515815260200190565b60405180910390a3801561267d576040517fb4d87a12000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0383811660248301527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169063b4d87a1290604401611f7b565b6040517fd48d8423000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0383811660248301527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169063d48d84239060440161201c565b80600003610ec7576040517f1f2a200500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116610ec7576040517f66928bea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600064e8d4a510006127756118b3565b61277f919061300f565b61278a906001613076565b6fffffffffffffffffffffffffffffffff16905090565b6040517f42966c68000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b0316906342966c6890602401612498565b6000805485825260036020819052604083206001600160a01b03909216916360c0c0ba913491899186908a9081111561284257612842612dd2565b600381111561285357612853612dd2565b81526020019081526020016000205487876040518663ffffffff1660e01b8152600401612883949392919061309e565b60206040518083038185885af11580156128a1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906128c69190612e8e565b95945050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16610b09576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612932612a34565b610b09576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61297061292a565b61297982612a53565b612981612a5b565b61136681612a63565b808251146113665781516040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101919091526024810182905260440161096b565b601181810151906129e2838261298a565b50919050565b6021810151604180830151906129fe848261298a565b50915091565b602181015160358083015190600090612a1d8582612a6b565b9092509050612a2c858261298a565b509193909250565b6000612a3e611dbf565b5468010000000000000000900460ff16919050565b61115d61292a565b610b0961292a565b610e7e61292a565b600181830181015160009183019060fe811615612ab9576040517f63e37c1800000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161096b565b60ff1694909350915050565b600080600060408486031215612ada57600080fd5b83359250602084013567ffffffffffffffff811115612af857600080fd5b8401601f81018613612b0957600080fd5b803567ffffffffffffffff811115612b2057600080fd5b866020828401011115612b3257600080fd5b939660209190910195509293505050565b80356001600160a01b0381168114612b5a57600080fd5b919050565b60008060008060808587031215612b7557600080fd5b8435935060208501359250612b8c60408601612b43565b9150612b9a60608601612b43565b905092959194509250565b60008060008060008060c08789031215612bbe57600080fd5b86359550612bce60208801612b43565b945060408701359350612be360608801612b43565b9250612bf160808801612b43565b9150612bff60a08801612b43565b90509295509295509295565b600080600060608486031215612c2057600080fd5b8335925060208401359150612c3760408501612b43565b90509250925092565b803560048110612b5a57600080fd5b600080600060608486031215612c6457600080fd5b83359250612c7460208501612c40565b929592945050506040919091013590565b60008060408385031215612c9857600080fd5b82359150612ca860208401612c40565b90509250929050565b600060208284031215612cc357600080fd5b61124982612b43565b600060208284031215612cde57600080fd5b5035919050565b8015158114610ec757600080fd5b60008060008060808587031215612d0957600080fd5b612d1285612b43565b935060208501359250612d2760408601612b43565b91506060850135612d3781612ce5565b939692955090935050565b600080600060608486031215612d5757600080fd5b612d6084612b43565b9250612d6e60208501612b43565b9150612c3760408501612b43565b600080600060608486031215612d9157600080fd5b612d9a84612b43565b925060208401359150612c3760408501612b43565b60008060408385031215612dc257600080fd5b82359150612ca860208401612b43565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60005b83811015612e1c578181015183820152602001612e04565b50506000910152565b60008151808452612e3d816020860160208601612e01565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b8381528260208201526060604082015260006128c66060830184612e25565b600060208284031215612ea057600080fd5b5051919050565b600060208284031215612eb957600080fd5b815161124981612ce5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611de457611de4612ec4565b600060208284031215612f1857600080fd5b81516fffffffffffffffffffffffffffffffff8116811461124957600080fd5b600060048810612f71577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b5060f89690961b86526001860194909452606092831b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602187015291831b8216603586015290911b16604983015260801b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016605d820152606d0190565b60008251613005818460208701612e01565b9190910192915050565b60006fffffffffffffffffffffffffffffffff831680613058577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b806fffffffffffffffffffffffffffffffff84160491505092915050565b6fffffffffffffffffffffffffffffffff8181168382160190811115611de457611de4612ec4565b8481528360208201526001600160a01b03831660408201526080606082015260006109ad6080830184612e2556fea2646970667358221220ec128f9cf1bec45afe6a7c1c8315eb30685d98113dfdcdaa462200fb8a039e8264736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278

-----Decoded View---------------
Arg [0] : mToken_ (address): 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b
Arg [1] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [2] : swapFacility_ (address): 0xB6807116b3B1B321a390594e31ECD6e0076f6278

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b
Arg [1] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [2] : 000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278


Block Transaction Gas Used Reward
view all blocks sequenced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.