ETH Price: $2,962.53 (+0.45%)

Contract

0x58a3A9C561591bab0dd11110EcA755EA455f1841

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
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630792026-01-24 15:10:2650 secs ago1769267426
0x58a3A9C5...A455f1841
0 ETH
282630512026-01-24 15:09:281 min ago1769267368
0x58a3A9C5...A455f1841
0 ETH
282630512026-01-24 15:09:281 min ago1769267368
0x58a3A9C5...A455f1841
0 ETH
282630512026-01-24 15:09:281 min ago1769267368
0x58a3A9C5...A455f1841
0 ETH
282630512026-01-24 15:09:281 min ago1769267368
0x58a3A9C5...A455f1841
0 ETH
282630512026-01-24 15:09:281 min ago1769267368
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630492026-01-24 15:09:241 min ago1769267364
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
282630452026-01-24 15:09:122 mins ago1769267352
0x58a3A9C5...A455f1841
0 ETH
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MUSD

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 999999 runs

Other Settings:
london EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { PausableUpgradeable } from "../lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol";

import { MYieldToOne } from "../lib/evm-m-extensions/src/projects/yieldToOne/MYieldToOne.sol";

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

/**

███╗   ███╗███████╗████████╗ █████╗ ███╗   ███╗ █████╗ ███████╗██╗  ██╗    ██╗   ██╗███████╗██████╗
████╗ ████║██╔════╝╚══██╔══╝██╔══██╗████╗ ████║██╔══██╗██╔════╝██║ ██╔╝    ██║   ██║██╔════╝██╔══██╗
██╔████╔██║█████╗     ██║   ███████║██╔████╔██║███████║███████╗█████╔╝     ██║   ██║███████╗██║  ██║
██║╚██╔╝██║██╔══╝     ██║   ██╔══██║██║╚██╔╝██║██╔══██║╚════██║██╔═██╗     ██║   ██║╚════██║██║  ██║
██║ ╚═╝ ██║███████╗   ██║   ██║  ██║██║ ╚═╝ ██║██║  ██║███████║██║  ██╗    ╚██████╔╝███████║██████╔╝
╚═╝     ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝     ╚═════╝ ╚══════╝╚═════╝

*/

/**
 * @title  MUSD
 * @notice M extension for the MUSD token.
 * @author M0 Labs
 */
contract MUSD is IMUSD, MYieldToOne, PausableUpgradeable {
    /* ============ Variables ============ */

    /// @inheritdoc IMUSD
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    /// @inheritdoc IMUSD
    bytes32 public constant FORCED_TRANSFER_MANAGER_ROLE = keccak256("FORCED_TRANSFER_MANAGER_ROLE");

    /* ============ Constructor ============ */

    /**
     * @custom:oz-upgrades-unsafe-allow constructor
     * @notice Constructs MUSD Implementation contract
     * @dev    `_disableInitializers()` is called in the inherited MExtension's constructor.
     * @param  mToken       The address of the MToken
     * @param  swapFacility The address of the SwapFacility
     */
    constructor(address mToken, address swapFacility) MYieldToOne(mToken, swapFacility) {}

    /* ============ Initializer ============ */

    /**
     * @dev   Initializes the MUSD token.
     * @param yieldRecipient        The address of a yield destination.
     * @param admin                 The address of an admin.
     * @param freezeManager         The address of a freeze manager.
     * @param yieldRecipientManager The address of a yield recipient setter.
     * @param pauser                The address of a pauser.
     */
    function initialize(
        address yieldRecipient,
        address admin,
        address freezeManager,
        address yieldRecipientManager,
        address pauser,
        address forcedTransferManager
    ) external initializer {
        if (pauser == address(0)) revert ZeroPauser();
        if (forcedTransferManager == address(0)) revert ZeroForcedTransferManager();

        __MYieldToOne_init("MetaMask USD", "mUSD", yieldRecipient, admin, freezeManager, yieldRecipientManager);
        __Pausable_init();

        _grantRole(PAUSER_ROLE, pauser);
        _grantRole(FORCED_TRANSFER_MANAGER_ROLE, forcedTransferManager);
    }

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

    /// @inheritdoc IMUSD
    function pause() external onlyRole(PAUSER_ROLE) {
        _pause();
    }

    /// @inheritdoc IMUSD
    function unpause() external onlyRole(PAUSER_ROLE) {
        _unpause();
    }

    /// @inheritdoc IMUSD
    function forceTransfer(
        address frozenAccount,
        address recipient,
        uint256 amount
    ) external onlyRole(FORCED_TRANSFER_MANAGER_ROLE) {
        _forceTransfer(frozenAccount, recipient, amount);
    }

    /// @inheritdoc IMUSD
    function forceTransfers(
        address[] calldata frozenAccounts,
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external onlyRole(FORCED_TRANSFER_MANAGER_ROLE) {
        if (frozenAccounts.length != recipients.length || frozenAccounts.length != amounts.length) {
            revert ArrayLengthMismatch();
        }

        for (uint256 i; i < frozenAccounts.length; ++i) {
            _forceTransfer(frozenAccounts[i], recipients[i], amounts[i]);
        }
    }

    /* ============ Hooks For Internal Interactive Functions ============ */

    /**
     * @dev   Hook called before wrapping M into mUSD.
     * @param account   The account from which M is deposited.
     * @param recipient The account receiving the minted mUSD.
     * @param amount    The amount of tokens to wrap.
     */
    function _beforeWrap(address account, address recipient, uint256 amount) internal view override {
        _requireNotPaused();

        super._beforeWrap(account, recipient, amount);
    }

    /**
     * @dev   Hook called before unwrapping mUSD.
     * @param account The account from which mUSD is burned.
     * @param amount  The amount of tokens to unwrap.
     */
    function _beforeUnwrap(address account, uint256 amount) internal view override {
        _requireNotPaused();

        super._beforeUnwrap(account, amount);
    }

    /**
     * @dev   Hook called before transferring mUSD.
     * @param sender    The address from which the tokens are being transferred.
     * @param recipient The address to which the tokens are being transferred.
     * @param amount    The amount of tokens to transfer.
     */
    function _beforeTransfer(address sender, address recipient, uint256 amount) internal view override {
        _requireNotPaused();

        super._beforeTransfer(sender, recipient, amount);
    }

    /**
     * @dev Hook called before claiming yield.
     * @dev MUST only be callable by the `YIELD_RECIPIENT_MANAGER_ROLE`.
     * @dev Addresses with the `YIELD_RECIPIENT_MANAGER_ROLE`
     *      are still able to claim yield when the contract is paused.
     */
    function _beforeClaimYield() internal view override onlyRole(YIELD_RECIPIENT_MANAGER_ROLE) {}

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

    /**
     * @dev   Internal ERC20 force transfer function to seize funds from a frozen account.
     * @param frozenAccount The frozen account from which tokens are seized.
     * @param recipient     The recipient's address.
     * @param amount        The amount to be transferred.
     * @dev   Force transfer is only allowed for frozen accounts.
     * @dev   No `_beforeTransfer` checks apply to forced transfers; ignore checks for paused and frozen states.
     * @dev   Since this function can only be called by the `FORCED_TRANSFER_MANAGER_ROLE`,
     *        we do not check if the recipient is frozen.
     */
    function _forceTransfer(address frozenAccount, address recipient, uint256 amount) internal {
        _revertIfInvalidRecipient(recipient);
        _revertIfNotFrozen(frozenAccount);

        emit Transfer(frozenAccount, recipient, amount);
        emit ForcedTransfer(frozenAccount, recipient, msg.sender, amount);

        if (amount == 0) return;

        _revertIfInsufficientBalance(frozenAccount, amount);

        _update(frozenAccount, recipient, amount);
    }
}

// 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: BUSL-1.1

pragma solidity 0.8.26;

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

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

import { Freezable } from "../../components/Freezable.sol";
import { MExtension } from "../../MExtension.sol";

abstract contract MYieldToOneStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.MYieldToOne
    struct MYieldToOneStorageStruct {
        uint256 totalSupply;
        address yieldRecipient;
        mapping(address account => uint256 balance) balanceOf;
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.MYieldToOne")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _M_YIELD_TO_ONE_STORAGE_LOCATION =
        0xee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100;

    function _getMYieldToOneStorageLocation() internal pure returns (MYieldToOneStorageStruct storage $) {
        assembly {
            $.slot := _M_YIELD_TO_ONE_STORAGE_LOCATION
        }
    }
}

/**
 * @title  MYieldToOne
 * @notice Upgradeable ERC20 Token contract for wrapping M into a non-rebasing token
 *         with yield claimable by a single recipient.
 * @author M0 Labs
 */
contract MYieldToOne is IMYieldToOne, MYieldToOneStorageLayout, MExtension, Freezable {
    /* ============ Variables ============ */

    /// @inheritdoc IMYieldToOne
    bytes32 public constant YIELD_RECIPIENT_MANAGER_ROLE = keccak256("YIELD_RECIPIENT_MANAGER_ROLE");

    /* ============ Constructor ============ */

    /**
     * @custom:oz-upgrades-unsafe-allow constructor
     * @notice Constructs MYieldToOne Implementation contract
     * @dev    Sets immutable storage.
     * @param  mToken       The address of $M token.
     * @param  swapFacility The address of Swap Facility.
     */
    constructor(address mToken, address swapFacility) MExtension(mToken, swapFacility) {}

    /* ============ Initializer ============ */

    /**
     * @dev   Initializes the M extension token with yield claimable by a single recipient.
     * @param name                  The name of the token (e.g. "M Yield to One").
     * @param symbol                The symbol of the token (e.g. "MYO").
     * @param yieldRecipient_       The address of a yield destination.
     * @param admin                 The address of an admin.
     * @param freezeManager         The address of a freeze manager.
     * @param yieldRecipientManager The address of a yield recipient setter.
     */
    function initialize(
        string memory name,
        string memory symbol,
        address yieldRecipient_,
        address admin,
        address freezeManager,
        address yieldRecipientManager
    ) public virtual initializer {
        __MYieldToOne_init(name, symbol, yieldRecipient_, admin, freezeManager, yieldRecipientManager);
    }

    /**
     * @notice Initializes the MYieldToOne token.
     * @param name                  The name of the token (e.g. "M Yield to One").
     * @param symbol                The symbol of the token (e.g. "MYO").
     * @param yieldRecipient_       The address of a yield destination.
     * @param admin                 The address of an admin.
     * @param freezeManager         The address of a freeze manager.
     * @param yieldRecipientManager The address of a yield recipient setter.
     */
    function __MYieldToOne_init(
        string memory name,
        string memory symbol,
        address yieldRecipient_,
        address admin,
        address freezeManager,
        address yieldRecipientManager
    ) internal onlyInitializing {
        if (yieldRecipientManager == address(0)) revert ZeroYieldRecipientManager();
        if (admin == address(0)) revert ZeroAdmin();

        __MExtension_init(name, symbol);
        __Freezable_init(freezeManager);

        _setYieldRecipient(yieldRecipient_);

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(YIELD_RECIPIENT_MANAGER_ROLE, yieldRecipientManager);
    }

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

    /// @inheritdoc IMYieldToOne
    function claimYield() public returns (uint256) {
        _beforeClaimYield();

        uint256 yield_ = yield();

        if (yield_ == 0) return 0;

        emit YieldClaimed(yield_);

        _mint(yieldRecipient(), yield_);

        return yield_;
    }

    /// @inheritdoc IMYieldToOne
    function setYieldRecipient(address account) external onlyRole(YIELD_RECIPIENT_MANAGER_ROLE) {
        // Claim yield for the previous yield recipient.
        claimYield();

        _setYieldRecipient(account);
    }

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

    /// @inheritdoc IERC20
    function balanceOf(address account) public view override returns (uint256) {
        return _getMYieldToOneStorageLocation().balanceOf[account];
    }

    /// @inheritdoc IERC20
    function totalSupply() public view returns (uint256) {
        return _getMYieldToOneStorageLocation().totalSupply;
    }

    /// @inheritdoc IMYieldToOne
    function yield() public view returns (uint256) {
        unchecked {
            uint256 balance_ = _mBalanceOf(address(this));
            uint256 totalSupply_ = totalSupply();

            return balance_ > totalSupply_ ? balance_ - totalSupply_ : 0;
        }
    }

    /// @inheritdoc IMYieldToOne
    function yieldRecipient() public view returns (address) {
        return _getMYieldToOneStorageLocation().yieldRecipient;
    }

    /* ============ Hooks For Internal Interactive Functions ============ */

    /**
     * @dev    Hooks called before approval of M extension spend.
     * @param  account The account from which M is deposited.
     * @param  spender The account spending M Extension token.
     */
    function _beforeApprove(address account, address spender, uint256 /* amount */) internal view virtual override {
        FreezableStorageStruct storage $ = _getFreezableStorageLocation();

        _revertIfFrozen($, account);
        _revertIfFrozen($, spender);
    }

    /**
     * @dev    Hooks called before wrapping M into M Extension token.
     * @param  account   The account from which M is deposited.
     * @param  recipient The account receiving the minted M Extension token.
     */
    function _beforeWrap(address account, address recipient, uint256 /* amount */) internal view virtual override {
        FreezableStorageStruct storage $ = _getFreezableStorageLocation();

        _revertIfFrozen($, account);
        _revertIfFrozen($, recipient);
    }

    /**
     * @dev   Hook called before unwrapping M Extension token.
     * @param account The account from which M Extension token is burned.
     */
    function _beforeUnwrap(address account, uint256 /* amount */) internal view virtual override {
        _revertIfFrozen(_getFreezableStorageLocation(), account);
    }

    /**
     * @dev   Hook called before transferring M Extension token.
     * @param sender    The address from which the tokens are being transferred.
     * @param recipient The address to which the tokens are being transferred.
     */
    function _beforeTransfer(address sender, address recipient, uint256 /* amount */) internal view virtual override {
        FreezableStorageStruct storage $ = _getFreezableStorageLocation();

        _revertIfFrozen($, msg.sender);

        _revertIfFrozen($, sender);
        _revertIfFrozen($, recipient);
    }

    /**
     * @dev   Hook called before claiming yield from the M Extension token. To be overridden in derived extensions.
     */
    function _beforeClaimYield() internal view virtual {}

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

    /**
     * @dev   Mints `amount` tokens to `recipient`.
     * @param recipient The address whose account balance will be incremented.
     * @param amount    The present amount of tokens to mint.`
     */
    function _mint(address recipient, uint256 amount) internal override {
        MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();

        // NOTE: Can be `unchecked` because the max amount of $M is never greater than `type(uint240).max`.
        unchecked {
            $.balanceOf[recipient] += amount;
            $.totalSupply += amount;
        }

        emit Transfer(address(0), recipient, amount);
    }

    /**
     * @dev   Burns `amount` tokens from `account`.
     * @param account The address whose account balance will be decremented.
     * @param amount  The present amount of tokens to burn.
     */
    function _burn(address account, uint256 amount) internal override {
        MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();

        // NOTE: Can be `unchecked` because `_revertIfInsufficientBalance` is used in MExtension.
        unchecked {
            $.balanceOf[account] -= amount;
            $.totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev   Internal balance update function called on transfer.
     * @param sender    The sender's address.
     * @param recipient The recipient's address.
     * @param amount    The amount to be transferred.
     */
    function _update(address sender, address recipient, uint256 amount) internal override {
        MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();

        // NOTE: Can be `unchecked` because `_revertIfInsufficientBalance` for `sender` is used in MExtension.
        unchecked {
            $.balanceOf[sender] -= amount;
            $.balanceOf[recipient] += amount;
        }
    }

    /**
     * @dev Sets the yield recipient.
     * @param yieldRecipient_ The address of the new yield recipient.
     */
    function _setYieldRecipient(address yieldRecipient_) internal {
        if (yieldRecipient_ == address(0)) revert ZeroYieldRecipient();

        MYieldToOneStorageStruct storage $ = _getMYieldToOneStorageLocation();

        if (yieldRecipient_ == $.yieldRecipient) return;

        $.yieldRecipient = yieldRecipient_;

        emit YieldRecipientSet(yieldRecipient_);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

/**
 * @title  MUSD Interface
 * @author M0 Labs
 *
 */
interface IMUSD {
    /* ============ Events ============ */

    /**
     * @notice Emitted when tokens are forcefully transferred from a frozen account.
     * @param  frozenAccount The address of the frozen account.
     * @param  recipient The address of the recipient.
     * @param  forcedTransferManager The address of the force transfer manager that triggered the event.
     * @param  amount The amount of tokens transferred.
     */
    event ForcedTransfer(
        address indexed frozenAccount,
        address indexed recipient,
        address indexed forcedTransferManager,
        uint256 amount
    );

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

    /// @notice Emitted in constructor if Pauser is 0x0.
    error ZeroPauser();

    /// @notice Emitted in constructor if Force Transfer Manager is 0x0.
    error ZeroForcedTransferManager();

    /// @notice Emitted when the length of the input arrays do not match in `forceTransfer` method.
    error ArrayLengthMismatch();

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

    /**
     * @notice Pauses the contract.
     * @dev    Can only be called by an account with the PAUSER_ROLE.
     * @dev    When paused, wrap/unwrap and transfer of tokens are disabled.
     *         Approval is still enabled to allow users to change their allowances.
     *         Addresses with the FORCED_TRANSFER_MANAGER_ROLE can still transfer tokens from frozen accounts.
     *         Addresses with the FREEZE_MANAGER_ROLE can still freeze and unfreeze accounts.
     *         Addresses with the YIELD_RECIPIENT_MANAGER_ROLE can still claim yield.
     */
    function pause() external;

    /**
     * @notice Unpauses the contract.
     * @dev    Can only be called by an account with the PAUSER_ROLE.
     */
    function unpause() external;

    /**
     * @notice Forcefully transfers tokens from a frozen account to a recipient.
     * @dev    Can only be called by an account with the FORCED_TRANSFER_MANAGER_ROLE.
     * @param  frozenAccount The address of the frozen account.
     * @param  recipient The address of the recipient.
     * @param  amount The amount of tokens to transfer.
     */
    function forceTransfer(address frozenAccount, address recipient, uint256 amount) external;

    /**
     * @notice Forcefully transfers tokens from frozen accounts to recipients.
     * @dev    Can only be called by an account with the FORCED_TRANSFER_MANAGER_ROLE.
     * @param  frozenAccounts The addresses of the frozen accounts.
     * @param  recipients The addresses of the recipients.
     * @param  amounts The amounts of tokens to transfer.
     */
    function forceTransfers(
        address[] calldata frozenAccounts,
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external;

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

    /// @notice The role that can pause and unpause the contract.
    function PAUSER_ROLE() external view returns (bytes32);

    /// @notice The role that can force transfer tokens from frozen accounts.
    function FORCED_TRANSFER_MANAGER_ROLE() external view returns (bytes32);
}

// 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
        }
    }
}

// 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: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title M Extension where all yield is claimable by a single recipient.
 * @author M0 Labs
 */
interface IMYieldToOne {
    /* ============ Events ============ */

    /**
     * @notice Emitted when this contract's excess M is claimed.
     * @param  yield The amount of M yield claimed.
     */
    event YieldClaimed(uint256 yield);

    /**
     * @notice Emitted when the yield recipient is set.
     * @param  yieldRecipient The address of the new yield recipient.
     */
    event YieldRecipientSet(address indexed yieldRecipient);

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

    /// @notice Emitted in constructor if Yield Recipient is 0x0.
    error ZeroYieldRecipient();

    /// @notice Emitted in constructor if Yield Recipient Manager is 0x0.
    error ZeroYieldRecipientManager();

    /// @notice Emitted in constructor if Admin is 0x0.
    error ZeroAdmin();

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

    /// @notice Claims accrued yield to yield recipient.
    function claimYield() external returns (uint256);

    /**
     * @notice Sets the yield recipient.
     * @dev    MUST only be callable by the YIELD_RECIPIENT_MANAGER_ROLE.
     * @dev    SHOULD revert if `yieldRecipient` is 0x0.
     * @dev    SHOULD return early if the `yieldRecipient` is already the actual yield recipient.
     * @param  yieldRecipient The address of the new yield recipient.
     */
    function setYieldRecipient(address yieldRecipient) external;

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

    /// @notice The role that can manage the yield recipient.
    function YIELD_RECIPIENT_MANAGER_ROLE() external view returns (bytes32);

    /// @notice The amount of accrued yield.
    function yield() external view returns (uint256);

    /// @notice The address of the yield recipient.
    function yieldRecipient() external view returns (address);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

import {
    AccessControlUpgradeable
} from "../../lib/common/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";

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

abstract contract FreezableStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.Freezable
    struct FreezableStorageStruct {
        mapping(address account => bool isFrozen) isFrozen;
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.Freezable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _FREEZABLE_STORAGE_LOCATION =
        0x2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00;

    function _getFreezableStorageLocation() internal pure returns (FreezableStorageStruct storage $) {
        assembly {
            $.slot := _FREEZABLE_STORAGE_LOCATION
        }
    }
}

/**
 * @title Freezable
 * @notice Upgradeable contract that allows for the freezing of accounts.
 * @dev This contract is used to prevent certain accounts from interacting with the contract.
 * @author M0 Labs
 */
abstract contract Freezable is IFreezable, FreezableStorageLayout, AccessControlUpgradeable {
    /* ============ Variables ============ */

    /// @inheritdoc IFreezable
    bytes32 public constant FREEZE_MANAGER_ROLE = keccak256("FREEZE_MANAGER_ROLE");

    /* ============ Initializer ============ */

    /**
     * @notice Initializes the contract with the given freeze manager.
     * @param freezeManager The address of a freeze manager.
     */
    function __Freezable_init(address freezeManager) internal onlyInitializing {
        if (freezeManager == address(0)) revert ZeroFreezeManager();
        _grantRole(FREEZE_MANAGER_ROLE, freezeManager);
    }

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

    /// @inheritdoc IFreezable
    function freeze(address account) external onlyRole(FREEZE_MANAGER_ROLE) {
        _freeze(_getFreezableStorageLocation(), account);
    }

    /// @inheritdoc IFreezable
    function freezeAccounts(address[] calldata accounts) external onlyRole(FREEZE_MANAGER_ROLE) {
        FreezableStorageStruct storage $ = _getFreezableStorageLocation();

        for (uint256 i; i < accounts.length; ++i) {
            _freeze($, accounts[i]);
        }
    }

    /// @inheritdoc IFreezable
    function unfreeze(address account) external onlyRole(FREEZE_MANAGER_ROLE) {
        _unfreeze(_getFreezableStorageLocation(), account);
    }

    /// @inheritdoc IFreezable
    function unfreezeAccounts(address[] calldata accounts) external onlyRole(FREEZE_MANAGER_ROLE) {
        FreezableStorageStruct storage $ = _getFreezableStorageLocation();

        for (uint256 i; i < accounts.length; ++i) {
            _unfreeze($, accounts[i]);
        }
    }

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

    /// @inheritdoc IFreezable
    function isFrozen(address account) public view returns (bool) {
        return _getFreezableStorageLocation().isFrozen[account];
    }

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

    /**
     * @notice Internal function that freezes an account.
     * @param $ The storage location of the freezable contract.
     * @param account The account to freeze.
     */
    function _freeze(FreezableStorageStruct storage $, address account) internal {
        _revertIfFrozen($, account);

        $.isFrozen[account] = true;

        emit Frozen(account, block.timestamp);
    }

    /**
     * @notice Internal function that unfreezes an account.
     * @param $ The storage location of the freezable contract.
     * @param account The account to unfreeze.
     */
    function _unfreeze(FreezableStorageStruct storage $, address account) internal {
        _revertIfNotFrozen($, account);

        $.isFrozen[account] = false;

        emit Unfrozen(account, block.timestamp);
    }

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

    /**
     * @notice Internal function that reverts if an account is frozen.
     * @param $ The storage location of the freezable contract.
     * @param account The account to check.
     */
    function _revertIfFrozen(FreezableStorageStruct storage $, address account) internal view {
        if ($.isFrozen[account]) revert AccountFrozen(account);
    }

    /**
     * @notice Internal function that reverts if an account is frozen.
     * @param account The account to check.
     */
    function _revertIfFrozen(address account) internal view {
        if (_getFreezableStorageLocation().isFrozen[account]) revert AccountFrozen(account);
    }

    /**
     * @notice Internal function that reverts if an account is not frozen.
     * @param $ The storage location of the freezable contract.
     * @param account The account to check.
     */
    function _revertIfNotFrozen(FreezableStorageStruct storage $, address account) internal view {
        if (!$.isFrozen[account]) revert AccountNotFrozen(account);
    }

    /**
     * @notice Internal function that reverts if an account is not frozen.
     * @param account The account to check.
     */
    function _revertIfNotFrozen(address account) internal view {
        if (!_getFreezableStorageLocation().isFrozen[account]) revert AccountNotFrozen(account);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

import { ERC20ExtendedUpgradeable } from "../lib/common/src/ERC20ExtendedUpgradeable.sol";

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

import { IMTokenLike } from "./interfaces/IMTokenLike.sol";
import { IMExtension } from "./interfaces/IMExtension.sol";
import { ISwapFacility } from "./swap/interfaces/ISwapFacility.sol";

/**
 * @title  MExtension
 * @notice Upgradeable ERC20 Token contract for wrapping M into a non-rebasing token.
 * @author M0 Labs
 */
abstract contract MExtension is IMExtension, ERC20ExtendedUpgradeable {
    /* ============ Variables ============ */

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    /// @inheritdoc IMExtension
    address public immutable mToken;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    /// @inheritdoc IMExtension
    address public immutable swapFacility;

    /* ============ Modifiers ============ */

    /// @dev Modifier to check if caller is SwapFacility.
    modifier onlySwapFacility() {
        if (msg.sender != swapFacility) revert NotSwapFacility();
        _;
    }

    /* ============ Constructor ============ */

    /**
     * @custom:oz-upgrades-unsafe-allow constructor
     * @notice Constructs MExtension Implementation contract
     * @dev    Sets immutable storage.
     * @param  mToken_       The address of $M token.
     * @param  swapFacility_ The address of Swap Facility.
     */
    constructor(address mToken_, address swapFacility_) {
        _disableInitializers();

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

    /* ============ Initializer ============ */

    /**
     * @notice Initializes the generic M extension token.
     * @param name          The name of the token (e.g. "HALO USD").
     * @param symbol        The symbol of the token (e.g. "HUSD").
     */
    function __MExtension_init(string memory name, string memory symbol) internal onlyInitializing {
        __ERC20ExtendedUpgradeable_init(name, symbol, 6);
    }

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

    /// @inheritdoc IMExtension
    function wrap(address recipient, uint256 amount) external onlySwapFacility {
        // NOTE: `msg.sender` is always SwapFacility contract.
        //       `ISwapFacility.msgSender()` is used to ensure that the original caller is passed to `_beforeWrap`.
        _wrap(ISwapFacility(msg.sender).msgSender(), recipient, amount);
    }

    /// @inheritdoc IMExtension
    function unwrap(address /* recipient */, uint256 amount) external onlySwapFacility {
        // NOTE: `msg.sender` is always SwapFacility contract.
        //       `ISwapFacility.msgSender()` is used to ensure that the original caller is passed to `_beforeUnwrap`.
        // NOTE: `recipient` is not used in this function as the $M is always sent to SwapFacility contract.
        _unwrap(ISwapFacility(msg.sender).msgSender(), amount);
    }

    /// @inheritdoc IMExtension
    function enableEarning() external virtual {
        if (isEarningEnabled()) revert EarningIsEnabled();

        emit EarningEnabled(currentIndex());

        IMTokenLike(mToken).startEarning();
    }

    /// @inheritdoc IMExtension
    function disableEarning() external virtual {
        if (!isEarningEnabled()) revert EarningIsDisabled();

        emit EarningDisabled(currentIndex());

        IMTokenLike(mToken).stopEarning(address(this));
    }

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

    /// @inheritdoc IMExtension
    function currentIndex() public view virtual returns (uint128) {
        return IMTokenLike(mToken).currentIndex();
    }

    /// @inheritdoc IMExtension
    function isEarningEnabled() public view virtual returns (bool) {
        return IMTokenLike(mToken).isEarning(address(this));
    }

    /// @inheritdoc IERC20
    function balanceOf(address account) public view virtual returns (uint256);

    /* ============ Hooks For Internal Interactive Functions ============ */

    /**
     * @dev   Hook called before approval of M Extension token.
     * @param account   The sender's address.
     * @param spender   The spender address.
     * @param amount    The amount to be approved.
     */
    function _beforeApprove(address account, address spender, uint256 amount) internal virtual {}

    /**
     * @dev    Hook called before wrapping M into M Extension token.
     * @param  account   The account from which M is deposited.
     * @param  recipient The account receiving the minted M Extension token.
     * @param  amount    The amount of M deposited.
     */
    function _beforeWrap(address account, address recipient, uint256 amount) internal virtual {}

    /**
     * @dev   Hook called before unwrapping M Extension token.
     * @param account   The account from which M Extension token is burned.
     * @param amount    The amount of M Extension token burned.
     */
    function _beforeUnwrap(address account, uint256 amount) internal virtual {}

    /**
     * @dev   Hook called before transferring M Extension token.
     * @param sender    The sender's address.
     * @param recipient The recipient's address.
     * @param amount    The amount to be transferred.
     */
    function _beforeTransfer(address sender, address recipient, uint256 amount) internal virtual {}

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

    /**
     * @dev Approve `spender` to spend `amount` of tokens from `account`.
     * @param account The address approving the allowance.
     * @param spender The address approved to spend the tokens.
     * @param amount  The amount of tokens being approved for spending.
     */
    function _approve(address account, address spender, uint256 amount) internal override {
        // NOTE: Add extension-specific checks before approval.
        _beforeApprove(account, spender, amount);

        super._approve(account, spender, amount);
    }

    /**
     * @dev    Wraps `amount` M from `account` into M Extension for `recipient`.
     * @param  account   The original caller of SwapFacility functions.
     * @param  recipient The account receiving the minted M Extension token.
     * @param  amount    The amount of M deposited.
     */
    function _wrap(address account, address recipient, uint256 amount) internal {
        _revertIfInvalidRecipient(recipient);
        _revertIfInsufficientAmount(amount);

        // NOTE: Add extension-specific checks before wrapping.
        _beforeWrap(account, recipient, amount);

        // NOTE: `msg.sender` is always SwapFacility contract.
        // NOTE: The behavior of `IMTokenLike.transferFrom` is known, so its return can be ignored.
        IMTokenLike(mToken).transferFrom(msg.sender, address(this), amount);

        // NOTE: This method is overridden by the inheriting M Extension contract.
        // NOTE: Mints precise amount of $M Extension token to `recipient`.
        //       Option 1: $M transfer from an $M earner to another $M earner ($M Extension in earning state): rounds up → rounds up,
        //                 0, 1, or XX extra wei may be locked in M Extension compared to the minted amount of $M Extension token.
        //       Option 2: $M transfer from an $M non-earner to an $M earner ($M Extension in earning state): precise $M transfer → rounds down,
        //                 0, -1, or -XX wei may be locked in $M Extension compared to the minted amount of $M Extension token.
        //
        _mint(recipient, amount);
    }

    /**
     * @dev    Unwraps `amount` M Extension token from `account` into $M and transfers to SwapFacility.
     * @param  account   The original caller of SwapFacility functions.
     * @param  amount    The amount of M Extension token burned.
     */
    function _unwrap(address account, uint256 amount) internal {
        _revertIfInsufficientAmount(amount);

        // NOTE: Add extension-specific checks before unwrapping.
        _beforeUnwrap(account, amount);

        _revertIfInsufficientBalance(msg.sender, amount);

        // NOTE: This method will be overridden by the inheriting M Extension contract.
        // NOTE: Computes the actual decrease in the $M balance of the $M Extension contract.
        //       Option 1: $M transfer from an $M earner ($M Extension in earning state) to another $M earner: round up → rounds up.
        //       Option 2: $M transfer from an $M earner ($M Extension in earning state) to an $M non-earner: round up → precise $M transfer.
        //       In both cases, 0, 1, or XX extra wei may be deducted from the $M Extension contract's $M balance compared to the burned amount of $M Extension token.
        // NOTE: Always burn from SwapFacility as it is the only contract that can call this function.
        _burn(msg.sender, amount);

        // NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored.
        // NOTE: `msg.sender` is always SwapFacility contract.
        IMTokenLike(mToken).transfer(msg.sender, amount);
    }

    /**
     * @dev   Mints `amount` tokens to `recipient`.
     * @param recipient The address to which the tokens will be minted.
     * @param amount    The amount of tokens to mint.
     */
    function _mint(address recipient, uint256 amount) internal virtual;

    /**
     * @dev   Burns `amount` tokens from `account`.
     * @param account The address from which the tokens will be burned.
     * @param amount  The amount of tokens to burn.
     */
    function _burn(address account, uint256 amount) internal virtual;

    /**
     * @dev   Internal balance update function that needs to be implemented by the inheriting contract.
     * @param sender    The sender's address.
     * @param recipient The recipient's address.
     * @param amount    The amount to be transferred.
     */
    function _update(address sender, address recipient, uint256 amount) internal virtual;

    /**
     * @dev   Internal ERC20 transfer function.
     * @param sender    The sender's address.
     * @param recipient The recipient's address.
     * @param amount    The amount to be transferred.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal override {
        _revertIfInvalidRecipient(recipient);

        // NOTE: Add extension-specific checks before transfers.
        _beforeTransfer(sender, recipient, amount);

        emit Transfer(sender, recipient, amount);

        if (amount == 0) return;

        _revertIfInsufficientBalance(sender, amount);

        // NOTE: This method will be overridden by the inheriting M Extension contract.
        _update(sender, recipient, amount);
    }

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

    /**
     * @dev    Returns the M Token balance of `account`.
     * @param  account The account being queried.
     * @return balance The M Token balance of the account.
     */
    function _mBalanceOf(address account) internal view returns (uint256) {
        return IMTokenLike(mToken).balanceOf(account);
    }

    /**
     * @dev   Reverts if `recipient` is address(0).
     * @param recipient Address of a recipient.
     */
    function _revertIfInvalidRecipient(address recipient) internal pure {
        if (recipient == address(0)) revert InvalidRecipient(recipient);
    }

    /**
     * @dev   Reverts if `amount` is equal to 0.
     * @param amount Amount of token.
     */
    function _revertIfInsufficientAmount(uint256 amount) internal pure {
        if (amount == 0) revert InsufficientAmount(amount);
    }

    /**
     * @dev   Reverts if `account` balance is below `amount`.
     * @param account Address of an account.
     * @param amount  Amount to transfer or burn.
     */
    function _revertIfInsufficientBalance(address account, uint256 amount) internal view {
        uint256 balance = balanceOf(account);

        if (balance < amount) revert InsufficientBalance(account, balance, amount);
    }
}

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

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title Freezable interface.
 * @author M0 Labs
 */
interface IFreezable {
    /* ============ Events ============ */

    /**
     * @notice Emitted when an account is frozen.
     * @param account The address of the frozen account.
     * @param timestamp The timestamp at which the account was frozen.
     */
    event Frozen(address indexed account, uint256 timestamp);

    /**
     * @notice Emitted when an account is unfrozen.
     * @param account The address of the unfrozen account.
     * @param timestamp The timestamp at which the account was unfrozen.
     */
    event Unfrozen(address indexed account, uint256 timestamp);

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

    /**
     * @notice Emitted when a frozen account attempts to interact with the contract.
     * @param account The address of the frozen account.
     */
    error AccountFrozen(address account);

    /**
     * @notice Emitted when trying to unfreeze a non-frozen account.
     * @param account The address of the account that is not frozen.
     */
    error AccountNotFrozen(address account);

    /// @notice Emitted if no freeze manager is set.
    error ZeroFreezeManager();

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

    /**
     * @notice Freezes an account.
     * @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
     * @dev SHOULD revert if the account is already frozen.
     * @param account The address of the account to freeze.
     */
    function freeze(address account) external;

    /**
     * @notice Freezes multiple accounts.
     * @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
     * @dev SHOULD revert if any of the accounts are already frozen.
     * @param accounts The list of addresses to freeze.
     */
    function freezeAccounts(address[] calldata accounts) external;

    /**
     * @notice Unfreezes an account.
     * @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
     * @dev SHOULD revert if the account is not frozen.
     * @param account The address of the account to unfreeze.
     */
    function unfreeze(address account) external;

    /**
     * @notice Unfreezes multiple accounts.
     * @dev MUST only be callable by the FREEZE_MANAGER_ROLE.
     * @dev SHOULD revert if any of the accounts are not frozen.
     * @param accounts The list of addresses to unfreeze.
     */
    function unfreezeAccounts(address[] calldata accounts) external;

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

    /// @notice The role that can manage the freezelist.
    function FREEZE_MANAGER_ROLE() external view returns (bytes32);

    /**
     * @notice Returns whether an account is frozen or not.
     * @param account The address of the account to check.
     * @return True if the account is frozen, false otherwise.
     */
    function isFrozen(address account) external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.sol";

abstract contract ERC20ExtendedUpgradeableStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.ERC20Extended
    struct ERC20ExtendedStorageStruct {
        mapping(address account => mapping(address spender => uint256 allowance)) allowance;
        uint8 decimals;
        string symbol;
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.ERC20Extended")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _ERC20_EXTENDED_STORAGE_LOCATION =
        0xcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100;

    function _getERC20ExtendedStorageLocation() internal pure returns (ERC20ExtendedStorageStruct storage $) {
        assembly {
            $.slot := _ERC20_EXTENDED_STORAGE_LOCATION
        }
    }
}

/**
 * @title  An upgradeable ERC20 token extended with EIP-2612 permits for signed approvals
 *         (via EIP-712 and with EIP-1271 and EIP-5267 compatibility).
 * @author M0 Labs
 */
abstract contract ERC20ExtendedUpgradeable is
    ERC20ExtendedUpgradeableStorageLayout,
    ERC3009Upgradeable,
    IERC20Extended
{
    /* ============ Variables ============ */

    /**
     * @inheritdoc IERC20Extended
     * @dev Keeping this constant, despite `permit` parameter name differences, to ensure max EIP-2612 compatibility.
     *      keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
     */
    bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /* ============ Initializer ============ */

    function __ERC20ExtendedUpgradeable_init(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) internal onlyInitializing {
        __ERC3009Upgradeable_init(name_);

        ERC20ExtendedStorageStruct storage $ = _getERC20ExtendedStorageLocation();

        $.decimals = decimals_;
        $.symbol = symbol_;
    }

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

    /// @inheritdoc IERC20
    function approve(address spender_, uint256 amount_) external returns (bool) {
        _approve(msg.sender, spender_, amount_);
        return true;
    }

    /// @inheritdoc IERC20Extended
    function permit(
        address owner_,
        address spender_,
        uint256 value_,
        uint256 deadline_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external {
        _revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), v_, r_, s_);
    }

    /// @inheritdoc IERC20Extended
    function permit(
        address owner_,
        address spender_,
        uint256 value_,
        uint256 deadline_,
        bytes memory signature_
    ) external {
        _revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), signature_);
    }

    /// @inheritdoc IERC20
    function transfer(address recipient_, uint256 amount_) external returns (bool) {
        _transfer(msg.sender, recipient_, amount_);
        return true;
    }

    /// @inheritdoc IERC20
    function transferFrom(address sender_, address recipient_, uint256 amount_) external returns (bool) {
        ERC20ExtendedStorageStruct storage $ = _getERC20ExtendedStorageLocation();
        uint256 spenderAllowance_ = $.allowance[sender_][msg.sender]; // Cache `spenderAllowance_` to stack.

        if (spenderAllowance_ != type(uint256).max) {
            if (spenderAllowance_ < amount_) revert InsufficientAllowance(msg.sender, spenderAllowance_, amount_);

            unchecked {
                _setAllowance($, sender_, msg.sender, spenderAllowance_ - amount_);
            }
        }

        _transfer(sender_, recipient_, amount_);

        return true;
    }

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

    /// @inheritdoc IERC20
    function allowance(address account, address spender) public view returns (uint256) {
        return _getERC20ExtendedStorageLocation().allowance[account][spender];
    }

    /// @inheritdoc IERC20
    function decimals() external view virtual returns (uint8) {
        return _getERC20ExtendedStorageLocation().decimals;
    }

    /// @inheritdoc IERC20
    function name() external view virtual returns (string memory) {
        return _getERC712ExtendedStorageLocation().name;
    }

    /// @inheritdoc IERC20
    function symbol() external view virtual returns (string memory) {
        return _getERC20ExtendedStorageLocation().symbol;
    }

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

    /**
     * @dev Approve `spender_` to spend `amount_` of tokens from `account_`.
     * @param  account_ The address approving the allowance.
     * @param  spender_ The address approved to spend the tokens.
     * @param  amount_  The amount of tokens being approved for spending.
     */
    function _approve(address account_, address spender_, uint256 amount_) internal virtual {
        _setAllowance(_getERC20ExtendedStorageLocation(), account_, spender_, amount_);
        emit Approval(account_, spender_, amount_);
    }

    /**
     * @dev Set the `amount_` of tokens `spender_` is allowed to spend from `account_`.
     * @param $         ERC20Extended storage location.
     * @param  account_ The address for which the allowance is set.
     * @param  spender_ The address allowed to spend the tokens.
     * @param  amount_  The amount of tokens being allowed for spending.
     */
    function _setAllowance(
        ERC20ExtendedStorageStruct storage $,
        address account_,
        address spender_,
        uint256 amount_
    ) internal virtual {
        $.allowance[account_][spender_] = amount_;
    }

    /**
     * @dev    Performs the approval based on the permit info, validates the deadline, and returns the digest.
     * @param  owner_    The address of the account approving the allowance.
     * @param  spender_  The address of the account being allowed to spend the tokens.
     * @param  amount_   The amount of tokens being approved for spending.
     * @param  deadline_ The deadline by which the signature must be used.
     * @return digest_   The EIP-712 digest of the permit.
     */
    function _permitAndGetDigest(
        address owner_,
        address spender_,
        uint256 amount_,
        uint256 deadline_
    ) internal virtual returns (bytes32) {
        _revertIfExpired(deadline_);

        _approve(owner_, spender_, amount_);

        unchecked {
            // Nonce realistically cannot overflow.
            return
                _getDigest(
                    keccak256(
                        abi.encode(
                            PERMIT_TYPEHASH,
                            owner_,
                            spender_,
                            amount_,
                            _getStatefulERC712ExtendedStorageLocation().nonces[owner_]++,
                            deadline_
                        )
                    )
                );
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title  Subset of M Token interface required for source contracts.
 * @author M0 Labs
 */
interface IMTokenLike {
    /* ============ Custom Errors ============ */

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

    /// @notice Emitted when calling `startEarning` for an account not approved as earner by TTG.
    error NotApprovedEarner();

    /* ============ 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 Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner    The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender  The address of an account allowed to spend on behalf of `owner`.
     * @param  value    The amount of the allowance being approved.
     * @param  deadline The last timestamp where the signature is still valid.
     * @param  v        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner     The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender   The address of an account allowed to spend on behalf of `owner`.
     * @param  value     The amount of the allowance being approved.
     * @param  deadline  The last timestamp where the signature is still valid.
     * @param  signature An arbitrary signature (EIP-712).
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;

    /**
     * @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 success   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 success   Whether or not the transfer was successful.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

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

    /**
     * @notice Stops earning for `account`.
     * @dev    MUST revert if `account` is an approved earner in TTG Registrar.
     * @param  account The account to stop earning for.
     */
    function stopEarning(address account) external;

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

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

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

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

    /// @notice The current value of earner rate in basis points.
    function earnerRate() external view returns (uint32);

    /// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
    function PERMIT_TYPEHASH() external view returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol";

/**
 * @title  M Extension interface extending Extended ERC20,
 *         includes additional enable/disable earnings and index logic.
 * @author M0 Labs
 */
interface IMExtension is IERC20Extended {
    /* ============ Events ============ */

    /**
     * @notice Emitted when M extension earning is enabled.
     * @param  index The index at the moment earning is enabled.
     */
    event EarningEnabled(uint128 index);

    /**
     * @notice Emitted when M extension earning is disabled.
     * @param  index The index at the moment earning is disabled.
     */
    event EarningDisabled(uint128 index);

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

    /// @notice Emitted when performing an operation that is not allowed when earning is disabled.
    error EarningIsDisabled();

    /// @notice Emitted when performing an operation that is not allowed when earning is enabled.
    error EarningIsEnabled();

    /**
     * @notice Emitted when there is insufficient balance to decrement from `account`.
     * @param  account The account with insufficient balance.
     * @param  balance The balance of the account.
     * @param  amount  The amount to decrement.
     */
    error InsufficientBalance(address account, uint256 balance, uint256 amount);

    /// @notice Emitted in constructor if M Token is 0x0.
    error ZeroMToken();

    /// @notice Emitted in constructor if Swap Facility is 0x0.
    error ZeroSwapFacility();

    /// @notice Emitted in `wrap` and `unwrap` functions if the caller is not the Swap Facility.
    error NotSwapFacility();

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

    /**
     * @notice Enables earning of extension token if allowed by the TTG Registrar and if it has never been done.
     * @dev SHOULD be virtual to allow extensions to override it.
     */
    function enableEarning() external;

    /**
     * @notice Disables earning of extension token if disallowed by the TTG Registrar and if it has never been done.
     * @dev SHOULD be virtual to allow extensions to override it.
     */
    function disableEarning() external;

    /**
     * @notice Wraps `amount` M from the caller into extension token for `recipient`.
     * @dev    Can only be called by the SwapFacility.
     * @param  recipient The account receiving the minted M extension token.
     * @param  amount    The amount of M extension token minted.
     */
    function wrap(address recipient, uint256 amount) external;

    /**
     * @notice Unwraps `amount` extension token from the caller into M for `recipient`.
     * @dev    Can only be called by the SwapFacility.
     * @param  recipient The account receiving the withdrawn M,
     *         it will always be the SwapFacility (keep `recipient` for backward compatibility).
     * @param  amount    The amount of M extension token burned.
     */
    function unwrap(address recipient, uint256 amount) external;

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

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

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

    /**
     * @notice Whether M extension earning is enabled.
     * @dev SHOULD be virtual to allow extensions to override it.
     */
    function isEarningEnabled() external view returns (bool);

    /**
     * @notice Returns the current index for M extension earnings.
     * @dev SHOULD be virtual to allow extensions to override it.
     */
    function currentIndex() external view returns (uint128);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title  Swap Facility interface.
 * @author M0 Labs
 */
interface ISwapFacility {
    /* ============ Events ============ */

    /**
     * @notice Emitted when $M Extension is swapped for another $M Extension.
     * @param extensionIn  The address of the input $M Extension.
     * @param extensionOut The address of the output $M Extension.
     * @param amount       The amount swapped.
     * @param recipient    The address to receive the output $M Extension token.
     */
    event Swapped(address indexed extensionIn, address indexed extensionOut, uint256 amount, address indexed recipient);

    /**
     * @notice Emitted when $M token is swapped for $M Extension.
     * @param extensionOut The address of the output $M Extension.
     * @param amount       The amount swapped.
     * @param recipient    The address to receive the output $M Extension token.
     */
    event SwappedInM(address indexed extensionOut, uint256 amount, address indexed recipient);

    /**
     * @notice Emitted when $M Extension is swapped for $M token.
     * @param extensionIn  The address of the input $M Extension.
     * @param amount       The amount swapped.
     * @param recipient    The address to receive the $M token.
     */
    event SwappedOutM(address indexed extensionIn, uint256 amount, address indexed recipient);

    /**
     * @notice Emitted when an $M Extension is set as permissioned or not.
     * @param  extension The address of an $M Extension.
     * @param  allowed   True if the extension is allowed, false otherwise.
     */
    event PermissionedExtensionSet(address indexed extension, bool allowed);

    /**
     * @notice Emitted when a `swapper` is allowed or not to swap the permissioned `extension` from/to M.
     * @param  extension The address of an $M extension.
     * @param  swapper   The address of the swapper.
     * @param  allowed   True if the swapper is allowed, false otherwise.
     */
    event PermissionedMSwapperSet(address indexed extension, address indexed swapper, bool allowed);

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

    /// @notice Thrown in the constructor if $M Token is 0x0.
    error ZeroMToken();

    /// @notice Thrown in the constructor if Registrar is 0x0.
    error ZeroRegistrar();

    /// @notice Thrown in `setPermissionedMSwapper()` if the $M extension is 0x0.
    error ZeroExtension();

    /// @notice Thrown in `setPermissionedMSwapper()` if the swapper is 0x0.
    error ZeroSwapper();

    /// @notice Thrown in `swap` and `swapM` functions if the extension is not TTG approved earner.
    error NotApprovedExtension(address extension);

    /// @notice Thrown in `swapInM` and `swapOutM` functions if `swapper` is not approved to swap a permissioned `extension`.
    error NotApprovedPermissionedSwapper(address extension, address swapper);

    /// @notice Thrown in `swapInM` and `swapOutM` functions if `swapper` is not approved to swap the `extension`.
    error NotApprovedSwapper(address extension, address swapper);

    /// @notice Thrown in `swap` function if the extension is permissioned.
    error PermissionedExtension(address extension);

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

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

    /**
     * @notice Swaps one $M Extension to another using permit.
     * @param  extensionIn  The address of the $M Extension to swap from.
     * @param  extensionOut The address of the $M Extension to swap to.
     * @param  amount       The amount to swap.
     * @param  recipient    The address to receive the swapped $M Extension tokens.
     * @param  deadline     The last timestamp where the signature is still valid.
     * @param  v            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function swapWithPermit(
        address extensionIn,
        address extensionOut,
        uint256 amount,
        address recipient,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Swaps one $M Extension to another using permit.
     * @param  extensionIn  The address of the $M Extension to swap from.
     * @param  extensionOut The address of the $M Extension to swap to.
     * @param  amount       The amount to swap.
     * @param  recipient    The address to receive the swapped $M Extension tokens.
     * @param  deadline     The last timestamp where the signature is still valid.
     * @param  signature    An arbitrary signature (EIP-712).
     */
    function swapWithPermit(
        address extensionIn,
        address extensionOut,
        uint256 amount,
        address recipient,
        uint256 deadline,
        bytes calldata signature
    ) external;

    /**
     * @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 token to $M Extension using permit.
     * @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.
     * @param  deadline     The last timestamp where the signature is still valid.
     * @param  v            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s            An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function swapInMWithPermit(
        address extensionOut,
        uint256 amount,
        address recipient,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Swaps $M token to $M Extension using permit.
     * @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.
     * @param  deadline     The last timestamp where the signature is still valid.
     * @param  signature    An arbitrary signature (EIP-712).
     */
    function swapInMWithPermit(
        address extensionOut,
        uint256 amount,
        address recipient,
        uint256 deadline,
        bytes calldata signature
    ) 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;

    /**
     * @notice Swaps $M Extension to $M token using permit.
     * @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.
     * @param  deadline    The last timestamp where the signature is still valid.
     * @param  v           An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r           An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s           An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function swapOutMWithPermit(
        address extensionIn,
        uint256 amount,
        address recipient,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Swaps $M Extension to $M token using permit.
     * @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.
     * @param  deadline    The last timestamp where the signature is still valid.
     * @param  signature   An arbitrary signature (EIP-712).
     */
    function swapOutMWithPermit(
        address extensionIn,
        uint256 amount,
        address recipient,
        uint256 deadline,
        bytes calldata signature
    ) external;

    /**
     * @notice Sets whether the `extension` is permissioned.
     * @dev    MUST only be callable by an address with the `DEFAULT_ADMIN_ROLE` role.
     * @param  extension    The address of an $M Extension.
     * @param  permissioned True if the extension is permissioned, false otherwise.
     */
    function setPermissionedExtension(address extension, bool permissioned) external;

    /**
     * @notice Sets whether `swapper` is allowed to swap the permissioned `extension` from/to M.
     * @dev    MUST only be callable by an address with the `DEFAULT_ADMIN_ROLE` role.
     * @param  extension The address of an extension to set permission for.
     * @param  swapper   The address of the swapper to set permission for.
     * @param  allowed   True if the swapper is allowed, false otherwise.
     */
    function setPermissionedMSwapper(address extension, address swapper, bool allowed) external;

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

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

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

    /**
     * @notice Returns the address that called `swap` or `swapM`
     * @dev    Must be used instead of `msg.sender` in $M Extensions contracts to get the original sender.
     */
    function msgSender() external view returns (address msgSender);

    /**
     * @notice Checks if the extension is permissioned.
     * @param  extension The extension address to check.
     * @return true if allowed, false otherwise.
     */
    function isPermissionedExtension(address extension) external view returns (bool);

    /**
     * @notice Checks if `swapper` is allowed to swap the permissioned extension from/to M.
     * @param  extension The $M extension address.
     * @param  swapper   The swapper address to check.
     * @return true if allowed, false otherwise.
     */
    function isPermissionedMSwapper(address extension, address swapper) external view returns (bool);

    /**
     * @notice Checks if `swapper` is allowed to swap the permissionless (common) extension from/to M.
     * @param  swapper   The swapper address to check.
     * @return true if allowed, false otherwise.
     */
    function isMSwapper(address swapper) external view returns (bool);

    /// @notice The parameter name in the Registrar that defines the earners list.
    function EARNERS_LIST_NAME() external pure returns (bytes32);

    /// @notice The parameter name in the Registrar that defines whether to ignore the earners list.
    function EARNERS_LIST_IGNORED_KEY() external pure returns (bytes32);

    /// @notice Swapper role for permissioned extensions.
    function M_SWAPPER_ROLE() external pure returns (bytes32);
}

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

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted to signal this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

import { Initializable } from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import { IERC3009 } from "./interfaces/IERC3009.sol";

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

abstract contract ERC3009UpgradeableStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.ERC3009
    struct ERC3009StorageStruct {
        mapping(address authorizer => mapping(bytes32 nonce => bool isNonceUsed)) authorizationState;
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.ERC3009")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _ERC3009_STORAGE_LOCATION =
        0x1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a4888600;

    function _getERC3009StorageLocation() internal pure returns (ERC3009StorageStruct storage $) {
        assembly {
            $.slot := _ERC3009_STORAGE_LOCATION
        }
    }
}

/**
 * @title  ERC3009 implementation allowing the transfer of fungible assets via a signed authorization.
 * @author M0 Labs
 * @dev    Inherits from ERC712ExtendedUpgradeable and StatefulERC712Upgradeable.
 */
abstract contract ERC3009Upgradeable is IERC3009, ERC3009UpgradeableStorageLayout, StatefulERC712Upgradeable {
    /* ============ Variables ============ */

    // solhint-disable-next-line max-line-length
    /// @dev        keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
    /// @inheritdoc IERC3009
    bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
        0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;

    // solhint-disable-next-line max-line-length
    /// @dev        keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
    /// @inheritdoc IERC3009
    bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
        0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;

    /**
     * @inheritdoc IERC3009
     * @dev        keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
     */
    bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
        0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;

    /* ============ Initializer ============ */

    /**
     * @notice Initializes the ERC3009Upgradeable contract.
     * @param  name_ The name of the contract.
     */
    function __ERC3009Upgradeable_init(string memory name_) internal onlyInitializing {
        __StatefulERC712ExtendedUpgradeable_init(name_);
    }

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

    /// @inheritdoc IERC3009
    function transferWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        bytes memory signature_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            signature_
        );

        _transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function transferWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        bytes32 r_,
        bytes32 vs_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            r_,
            vs_
        );

        _transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function transferWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            v_,
            r_,
            s_
        );

        _transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function receiveWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        bytes memory signature_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            signature_
        );

        _receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function receiveWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        bytes32 r_,
        bytes32 vs_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            r_,
            vs_
        );

        _receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function receiveWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external {
        _revertIfInvalidSignature(
            from_,
            _getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
            v_,
            r_,
            s_
        );

        _receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /// @inheritdoc IERC3009
    function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes memory signature_) external {
        _revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), signature_);
        _cancelAuthorization(authorizer_, nonce_);
    }

    /// @inheritdoc IERC3009
    function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes32 r_, bytes32 vs_) external {
        _revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), r_, vs_);
        _cancelAuthorization(authorizer_, nonce_);
    }

    /// @inheritdoc IERC3009
    function cancelAuthorization(address authorizer_, bytes32 nonce_, uint8 v_, bytes32 r_, bytes32 s_) external {
        _revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), v_, r_, s_);
        _cancelAuthorization(authorizer_, nonce_);
    }

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

    /// @inheritdoc IERC3009
    function authorizationState(address authorizer, bytes32 nonce) public view returns (bool) {
        return _getERC3009StorageLocation().authorizationState[authorizer][nonce];
    }

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

    /**
     * @dev   Common transfer function used by `transferWithAuthorization` and `_receiveWithAuthorization`.
     * @param from_        Payer's address (Authorizer).
     * @param to_          Payee's address.
     * @param value_       Amount to be transferred.
     * @param validAfter_  The time after which this is valid (unix time).
     * @param validBefore_ The time before which this is valid (unix time).
     * @param nonce_       Unique nonce.
     */
    function _transferWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_
    ) internal {
        if (block.timestamp <= validAfter_) revert AuthorizationNotYetValid(block.timestamp, validAfter_);
        if (block.timestamp >= validBefore_) revert AuthorizationExpired(block.timestamp, validBefore_);

        _revertIfAuthorizationAlreadyUsed(from_, nonce_);

        _getERC3009StorageLocation().authorizationState[from_][nonce_] = true;

        emit AuthorizationUsed(from_, nonce_);

        _transfer(from_, to_, value_);
    }

    /**
     * @dev   Common receive function used by `receiveWithAuthorization`.
     * @param from_        Payer's address (Authorizer).
     * @param to_          Payee's address.
     * @param value_       Amount to be transferred.
     * @param validAfter_  The time after which this is valid (unix time).
     * @param validBefore_ The time before which this is valid (unix time).
     * @param nonce_       Unique nonce.
     */
    function _receiveWithAuthorization(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_
    ) internal {
        if (msg.sender != to_) revert CallerMustBePayee(msg.sender, to_);

        _transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
    }

    /**
     * @dev   Common cancel function used by `cancelAuthorization`.
     * @param authorizer_ Authorizer's address.
     * @param nonce_      Nonce of the authorization.
     */
    function _cancelAuthorization(address authorizer_, bytes32 nonce_) internal {
        _revertIfAuthorizationAlreadyUsed(authorizer_, nonce_);

        _getERC3009StorageLocation().authorizationState[authorizer_][nonce_] = true;

        emit AuthorizationCanceled(authorizer_, nonce_);
    }

    /**
     * @dev   Internal ERC20 transfer function that needs to be implemented by the inheriting contract.
     * @param sender_    The sender's address.
     * @param recipient_ The recipient's address.
     * @param amount_    The amount to be transferred.
     */
    function _transfer(address sender_, address recipient_, uint256 amount_) internal virtual;

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

    /**
     * @dev    Returns the internal EIP-712 digest of a transferWithAuthorization call.
     * @param  from_        Payer's address (Authorizer).
     * @param  to_          Payee's address.
     * @param  value_       Amount to be transferred.
     * @param  validAfter_  The time after which this is valid (unix time).
     * @param  validBefore_ The time before which this is valid (unix time).
     * @param  nonce_       Unique nonce.
     * @return The internal EIP-712 digest.
     */
    function _getTransferWithAuthorizationDigest(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_
    ) internal view returns (bytes32) {
        return
            _getDigest(
                keccak256(
                    abi.encode(
                        TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                        from_,
                        to_,
                        value_,
                        validAfter_,
                        validBefore_,
                        nonce_
                    )
                )
            );
    }

    /**
     * @dev    Returns the internal EIP-712 digest of a receiveWithAuthorization call.
     * @param  from_        Payer's address (Authorizer).
     * @param  to_          Payee's address.
     * @param  value_       Amount to be transferred.
     * @param  validAfter_  The time after which this is valid (unix time).
     * @param  validBefore_ The time before which this is valid (unix time).
     * @param  nonce_       Unique nonce.
     * @return The internal EIP-712 digest.
     */
    function _getReceiveWithAuthorizationDigest(
        address from_,
        address to_,
        uint256 value_,
        uint256 validAfter_,
        uint256 validBefore_,
        bytes32 nonce_
    ) internal view returns (bytes32) {
        return
            _getDigest(
                keccak256(
                    abi.encode(
                        RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                        from_,
                        to_,
                        value_,
                        validAfter_,
                        validBefore_,
                        nonce_
                    )
                )
            );
    }

    /**
     * @dev    Returns the internal EIP-712 digest of a cancelAuthorization call.
     * @param  authorizer_ Authorizer's address.
     * @param  nonce_      Nonce of the authorization.
     * @return The internal EIP-712 digest.
     */
    function _getCancelAuthorizationDigest(address authorizer_, bytes32 nonce_) internal view returns (bytes32) {
        return _getDigest(keccak256(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer_, nonce_)));
    }

    /**
     * @dev   Reverts if the authorization is already used.
     * @param authorizer_ The authorizer's address.
     * @param nonce_      The nonce of the authorization.
     */
    function _revertIfAuthorizationAlreadyUsed(address authorizer_, bytes32 nonce_) internal view {
        if (authorizationState(authorizer_, nonce_)) revert AuthorizationAlreadyUsed(authorizer_, nonce_);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

import { IERC20 } from "./IERC20.sol";
import { IERC3009 } from "./IERC3009.sol";

/**
 * @title  An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712
 *         and with EIP-1271 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
 * @author M^0 Labs
 * @dev    The additional interface as defined by EIP-2612: https://eips.ethereum.org/EIPS/eip-2612
 */
interface IERC20Extended is IERC20, IERC3009 {
    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when spender's allowance is not sufficient.
     * @param  spender    Address that may be allowed to operate on tokens without being their owner.
     * @param  allowance  Amount of tokens a `spender` is allowed to operate with.
     * @param  needed     Minimum amount required to perform a transfer.
     */
    error InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @notice Revert message emitted when the transferred amount is insufficient.
     * @param  amount Amount transferred.
     */
    error InsufficientAmount(uint256 amount);

    /**
     * @notice Revert message emitted when the recipient of a token is invalid.
     * @param  recipient Address of the invalid recipient.
     */
    error InvalidRecipient(address recipient);

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

    /**
     * @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner    The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender  The address of an account allowed to spend on behalf of `owner`.
     * @param  value    The amount of the allowance being approved.
     * @param  deadline The last timestamp where the signature is still valid.
     * @param  v        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner     The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender   The address of an account allowed to spend on behalf of `owner`.
     * @param  value     The amount of the allowance being approved.
     * @param  deadline  The last timestamp where the signature is still valid.
     * @param  signature An arbitrary signature (EIP-712).
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;

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

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
    function PERMIT_TYPEHASH() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

/**
 * @title  Transfer via signed authorization following EIP-3009 standard.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-3009: https://eips.ethereum.org/EIPS/eip-3009
 */
interface IERC3009 is IStatefulERC712 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when an authorization has been canceled.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the canceled authorization.
     */
    event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);

    /**
     * @notice Emitted when an authorization has been used.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the used authorization.
     */
    event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);

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

    /**
     * @notice Emitted when an authorization has already been used.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the used authorization.
     */
    error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);

    /**
     * @notice Emitted when an authorization is expired.
     * @param  timestamp   Timestamp at which the transaction was submitted.
     * @param  validBefore Timestamp before which the authorization would have been valid.
     */
    error AuthorizationExpired(uint256 timestamp, uint256 validBefore);

    /**
     * @notice Emitted when an authorization is not yet valid.
     * @param  timestamp  Timestamp at which the transaction was submitted.
     * @param  validAfter Timestamp after which the authorization will be valid.
     */
    error AuthorizationNotYetValid(uint256 timestamp, uint256 validAfter);

    /**
     * @notice Emitted when the caller of `receiveWithAuthorization` is not the payee.
     * @param  caller Caller's address.
     * @param  payee  Payee's address.
     */
    error CallerMustBePayee(address caller, address payee);

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

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  signature   A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature
    ) external;

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  r           An ECDSA/secp256k1 signature parameter.
     * @param  vs          An ECDSA/secp256k1 short signature parameter.
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes32 r,
        bytes32 vs
    ) external;

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  v           v of the signature.
     * @param  r           r of the signature.
     * @param  s           s of the signature.
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  signature   A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  r           An ECDSA/secp256k1 signature parameter.
     * @param  vs          An ECDSA/secp256k1 short signature parameter.
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes32 r,
        bytes32 vs
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  v           v of the signature.
     * @param  r           r of the signature.
     * @param  s           s of the signature.
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  signature  A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, bytes memory signature) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  r          An ECDSA/secp256k1 signature parameter.
     * @param  vs         An ECDSA/secp256k1 short signature parameter.
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, bytes32 r, bytes32 vs) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  v          v of the signature.
     * @param  r          r of the signature.
     * @param  s          s of the signature.
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external;

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

    /**
     * @notice Returns the state of an authorization.
     * @dev    Nonces are randomly generated 32-byte data unique to the authorizer's address
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @return True if the nonce is used.
     */
    function authorizationState(address authorizer, bytes32 nonce) external view returns (bool);

    /// @notice Returns `transferWithAuthorization` typehash.
    function TRANSFER_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);

    /// @notice Returns `receiveWithAuthorization` typehash.
    function RECEIVE_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);

    /// @notice Returns `cancelAuthorization` typehash.
    function CANCEL_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

abstract contract StatefulERC712ExtendedUpgradeableStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.StatefulERC712Extended
    struct StatefulERC712ExtendedStorageStruct {
        mapping(address account => uint256 nonce) nonces; // Nonces for all signatures.
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.StatefulERC712Extended")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _STATEFUL_ERC712_EXTENDED_STORAGE_LOCATION =
        0x1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00;

    function _getStatefulERC712ExtendedStorageLocation()
        internal
        pure
        returns (StatefulERC712ExtendedStorageStruct storage $)
    {
        assembly {
            $.slot := _STATEFUL_ERC712_EXTENDED_STORAGE_LOCATION
        }
    }
}

/**
 * @title  Stateful and upgradeable extension for EIP-712 typed structured data hashing and signing with nonces.
 * @author M0 Labs
 * @dev    An abstract implementation to satisfy stateful EIP-712 with nonces.
 */
abstract contract StatefulERC712Upgradeable is
    StatefulERC712ExtendedUpgradeableStorageLayout,
    IStatefulERC712,
    ERC712ExtendedUpgradeable
{
    /* ============ Initializer ============ */

    /**
     * @notice Initializes the StatefulERC712Upgradeable contract.
     * @param  name The name of the contract.
     */
    function __StatefulERC712ExtendedUpgradeable_init(string memory name) internal onlyInitializing {
        __ERC712ExtendedUpgradeable_init(name);
    }

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

    /// @inheritdoc IStatefulERC712
    function nonces(address account) external view returns (uint256) {
        return _getStatefulERC712ExtendedStorageLocation().nonces[account];
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

/**
 * @title  Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
 * @author M^0 Labs
 */
interface IStatefulERC712 is IERC712Extended {
    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when a signing account's nonce is not the expected current nonce.
     * @param  nonce         The nonce used in the signature.
     * @param  expectedNonce The expected nonce to be used in a signature by the signing account.
     */
    error InvalidAccountNonce(uint256 nonce, uint256 expectedNonce);

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

    /**
     * @notice Returns the next nonce to be used in a signature by `account`.
     * @param  account The address of some account.
     * @return nonce   The next nonce to be used in a signature by `account`.
     */
    function nonces(address account) external view returns (uint256 nonce);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

import { Initializable } from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";

import { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";

import { SignatureChecker } from "./libs/SignatureChecker.sol";

abstract contract ERC712ExtendedUpgradeableStorageLayout {
    /// @custom:storage-location erc7201:M0.storage.ERC712Extended
    struct ERC712ExtendedStorageStruct {
        uint256 initialChainId;
        bytes32 initialDomainSeparator;
        string name;
    }

    // keccak256(abi.encode(uint256(keccak256("M0.storage.ERC712Extended")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _ERC712_EXTENDED_STORAGE_LOCATION =
        0x103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea100;

    function _getERC712ExtendedStorageLocation() internal pure returns (ERC712ExtendedStorageStruct storage $) {
        assembly {
            $.slot := _ERC712_EXTENDED_STORAGE_LOCATION
        }
    }
}

/**
 * @title  Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
 * @author M0 Labs
 * @dev    An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
 */
abstract contract ERC712ExtendedUpgradeable is ERC712ExtendedUpgradeableStorageLayout, IERC712Extended, Initializable {
    /* ============ Variables ============ */

    /// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 internal constant _EIP712_DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev keccak256("1")
    bytes32 internal constant _EIP712_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /* ============ Initializer ============ */

    /**
     * @notice Initializes the ERC712ExtendedUpgradeable contract.
     * @param  name_ The name of the contract.
     */
    function __ERC712ExtendedUpgradeable_init(string memory name_) internal onlyInitializing {
        ERC712ExtendedStorageStruct storage $ = _getERC712ExtendedStorageLocation();

        $.name = name_;
        $.initialChainId = block.chainid;
        $.initialDomainSeparator = _getDomainSeparator();
    }

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

    /// @inheritdoc IERC712Extended
    function eip712Domain()
        external
        view
        virtual
        returns (
            bytes1 fields_,
            string memory name_,
            string memory version_,
            uint256 chainId_,
            address verifyingContract_,
            bytes32 salt_,
            uint256[] memory extensions_
        )
    {
        return (
            hex"0f", // 01111
            _getERC712ExtendedStorageLocation().name,
            "1",
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /// @inheritdoc IERC712
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        ERC712ExtendedStorageStruct storage $ = _getERC712ExtendedStorageLocation();
        return block.chainid == $.initialChainId ? $.initialDomainSeparator : _getDomainSeparator();
    }

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

    /**
     * @dev    Computes the EIP-712 domain separator.
     * @return The EIP-712 domain separator.
     */
    function _getDomainSeparator() internal view returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    _EIP712_DOMAIN_HASH,
                    keccak256(bytes(_getERC712ExtendedStorageLocation().name)),
                    _EIP712_VERSION_HASH,
                    block.chainid,
                    address(this)
                )
            );
    }

    /**
     * @dev    Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  internalDigest_ The internal digest.
     * @return The digest to be signed.
     */
    function _getDigest(bytes32 internalDigest_) internal view returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), internalDigest_));
    }

    /**
     * @dev   Revert if the signature is expired.
     * @param expiry_ Timestamp at which the signature expires or max uint256 for no expiry.
     */
    function _revertIfExpired(uint256 expiry_) internal view {
        if (block.timestamp > expiry_) revert SignatureExpired(expiry_, block.timestamp);
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @dev   We first validate if the signature is a valid ECDSA signature and return early if it is the case.
     *        Then, we validate if it is a valid ERC-1271 signature, and return early if it is the case.
     *        If not, we revert with the error from the ECDSA signature validation.
     * @param signer_    The signer of the signature.
     * @param digest_    The digest that was signed.
     * @param signature_ The signature.
     */
    function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes memory signature_) internal view {
        SignatureChecker.Error error_ = SignatureChecker.validateECDSASignature(signer_, digest_, signature_);

        if (error_ == SignatureChecker.Error.NoError) return;

        if (SignatureChecker.isValidERC1271Signature(signer_, digest_, signature_)) return;

        _revertIfError(error_);
    }

    /**
     * @dev    Returns the signer of a signed digest, via EIP-712, and reverts if the signature is invalid.
     * @param  digest_ The digest that was signed.
     * @param  v_      v of the signature.
     * @param  r_      r of the signature.
     * @param  s_      s of the signature.
     * @return signer_ The signer of the digest.
     */
    function _getSignerAndRevertIfInvalidSignature(
        bytes32 digest_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) internal pure returns (address signer_) {
        SignatureChecker.Error error_;

        (error_, signer_) = SignatureChecker.recoverECDSASigner(digest_, v_, r_, s_);

        _revertIfError(error_);
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @param signer_ The signer of the signature.
     * @param digest_ The digest that was signed.
     * @param r_      An ECDSA/secp256k1 signature parameter.
     * @param vs_     An ECDSA/secp256k1 short signature parameter.
     */
    function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes32 r_, bytes32 vs_) internal pure {
        _revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, r_, vs_));
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @param signer_ The signer of the signature.
     * @param digest_ The digest that was signed.
     * @param v_      v of the signature.
     * @param r_      r of the signature.
     * @param s_      s of the signature.
     */
    function _revertIfInvalidSignature(
        address signer_,
        bytes32 digest_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) internal pure {
        _revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, v_, r_, s_));
    }

    /**
     * @dev   Revert if error.
     * @param error_ The SignatureChecker Error enum.
     */
    function _revertIfError(SignatureChecker.Error error_) private pure {
        if (error_ == SignatureChecker.Error.NoError) return;
        if (error_ == SignatureChecker.Error.InvalidSignature) revert InvalidSignature();
        if (error_ == SignatureChecker.Error.InvalidSignatureLength) revert InvalidSignatureLength();
        if (error_ == SignatureChecker.Error.InvalidSignatureS) revert InvalidSignatureS();
        if (error_ == SignatureChecker.Error.InvalidSignatureV) revert InvalidSignatureV();
        if (error_ == SignatureChecker.Error.SignerMismatch) revert SignerMismatch();

        revert InvalidSignature();
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

/**
 * @title  EIP-712 extended by EIP-5267.
 * @author M^0 Labs
 * @dev    The additional interface as defined by EIP-5267: https://eips.ethereum.org/EIPS/eip-5267
 */
interface IERC712Extended is IERC712 {
    /* ============ Events ============ */

    /// @notice MAY be emitted to signal that the domain could have changed.
    event EIP712DomainChanged();

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

    /// @notice Returns the fields and values that describe the domain separator used by this contract for EIP-712.
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  Typed structured data hashing and signing via EIP-712.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-712: https://eips.ethereum.org/EIPS/eip-712
 */
interface IERC712 {
    /* ============ Custom Errors ============ */

    /// @notice Revert message when an invalid signature is detected.
    error InvalidSignature();

    /// @notice Revert message when a signature with invalid length is detected.
    error InvalidSignatureLength();

    /// @notice Revert message when the S portion of a signature is invalid.
    error InvalidSignatureS();

    /// @notice Revert message when the V portion of a signature is invalid.
    error InvalidSignatureV();

    /**
     * @notice Revert message when a signature is being used beyond its deadline (i.e. expiry).
     * @param  deadline  The last timestamp where the signature is still valid.
     * @param  timestamp The current timestamp.
     */
    error SignatureExpired(uint256 deadline, uint256 timestamp);

    /// @notice Revert message when a recovered signer does not match the account being purported to have signed.
    error SignerMismatch();

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

    /// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

/**
 * @title  A library to handle ECDSA/secp256k1 and ERC1271 signatures, individually or in arbitrarily in combination.
 * @author M^0 Labs
 */
library SignatureChecker {
    /* ============ Enums ============ */

    /**
     * @notice An enum representing the possible errors that can be emitted during signature validation.
     * @param  NoError                No error occurred during signature validation.
     * @param  InvalidSignature       The signature is invalid.
     * @param  InvalidSignatureLength The signature length is invalid.
     * @param  InvalidSignatureS      The signature parameter S is invalid.
     * @param  InvalidSignatureV      The signature parameter V is invalid.
     * @param  SignerMismatch         The signer does not match the recovered signer.
     */
    enum Error {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV,
        SignerMismatch
    }

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

    /**
     * @dev    Returns whether a signature is valid (ECDSA/secp256k1 or ERC1271) for a signer and digest.
     * @dev    Signatures must not be used as unique identifiers since the `ecrecover` EVM opcode
     *         allows for malleable (non-unique) signatures.
     *         See https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array signature.
     * @return           Whether the signature is valid or not.
     */
    function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
        return isValidECDSASignature(signer, digest, signature) || isValidERC1271Signature(signer, digest, signature);
    }

    /**
     * @dev    Returns whether an ERC1271 signature is valid for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ERC1271 signature.
     * @return           Whether the signature is valid or not.
     */
    function isValidERC1271Signature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success_, bytes memory result_) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (digest, signature))
        );

        return
            success_ &&
            result_.length >= 32 &&
            abi.decode(result_, (bytes32)) == bytes32(IERC1271.isValidSignature.selector);
    }

    /**
     * @dev    Decodes an ECDSA/secp256k1 signature from a byte array to standard v, r, and s parameters.
     * @param  signature A byte array ECDSA/secp256k1 signature.
     * @return v         An ECDSA/secp256k1 signature parameter.
     * @return r         An ECDSA/secp256k1 signature parameter.
     * @return s         An ECDSA/secp256k1 signature parameter.
     */
    function decodeECDSASignature(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
        // ecrecover takes the signature parameters, and they can be decoded using assembly.
        /// @solidity memory-safe-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }
    }

    /**
     * @dev    Decodes an ECDSA/secp256k1 short signature as defined by EIP2098
     *         from a byte array to standard v, r, and s parameters.
     * @param  signature A byte array ECDSA/secp256k1 short signature.
     * @return r         An ECDSA/secp256k1 signature parameter.
     * @return vs        An ECDSA/secp256k1 short signature parameter.
     */
    function decodeShortECDSASignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 vs) {
        // ecrecover takes the signature parameters, and they can be decoded using assembly.
        /// @solidity memory-safe-assembly
        assembly {
            r := mload(add(signature, 0x20))
            vs := mload(add(signature, 0x40))
        }
    }

    /**
     * @dev    Returns whether an ECDSA/secp256k1 signature is valid for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     * @return           Whether the signature is valid or not.
     */
    function isValidECDSASignature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal pure returns (bool) {
        if (signature.length == 64) {
            (bytes32 r, bytes32 vs) = decodeShortECDSASignature(signature);
            return isValidECDSASignature(signer, digest, r, vs);
        }

        return validateECDSASignature(signer, digest, signature) == Error.NoError;
    }

    /**
     * @dev    Returns whether an ECDSA/secp256k1 short signature is valid for a signer and digest.
     * @param  signer The address of the account purported to have signed.
     * @param  digest The hash of the data that was signed.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  vs     An ECDSA/secp256k1 short signature parameter.
     * @return        Whether the signature is valid or not.
     */
    function isValidECDSASignature(address signer, bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (bool) {
        return validateECDSASignature(signer, digest, r, vs) == Error.NoError;
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 signature for some digest.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ECDSA/secp256k1 signature.
     * @return           An error, if any, that occurred during the signer recovery.
     * @return           The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(bytes32 digest, bytes memory signature) internal pure returns (Error, address) {
        if (signature.length != 65) return (Error.InvalidSignatureLength, address(0));

        (uint8 v, bytes32 r, bytes32 s) = decodeECDSASignature(signature);

        return recoverECDSASigner(digest, v, r, s);
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 short signature for some digest.
     * @dev    See https://eips.ethereum.org/EIPS/eip-2098
     * @param  digest The hash of the data that was signed.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  vs     An ECDSA/secp256k1 short signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     * @return        The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (Error, address) {
        unchecked {
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            return recoverECDSASigner(digest, v, r, s);
        }
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 signature for some digest.
     * @param  digest The hash of the data that was signed.
     * @param  v      An ECDSA/secp256k1 signature parameter.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  s      An ECDSA/secp256k1 signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     * @return signer The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (Error, address signer) {
        // Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}.
        if (uint256(s) > uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0))
            return (Error.InvalidSignatureS, address(0));

        if (v != 27 && v != 28) return (Error.InvalidSignatureV, address(0));

        signer = ecrecover(digest, v, r, s);

        return (signer == address(0)) ? (Error.InvalidSignature, address(0)) : (Error.NoError, signer);
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ERC1271 signature.
     * @return           An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, signature);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 short signature for a signer and digest.
     * @param  signer The address of the account purported to have signed.
     * @param  digest The hash of the data that was signed.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  vs     An ECDSA/secp256k1 short signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, r, vs);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
     * @param  signer The address of the account purported to have signed.
     * @param  digest The hash of the data that was signed.
     * @param  v      An ECDSA/secp256k1 signature parameter.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  s      An ECDSA/secp256k1 signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, v, r, s);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error if `signer` is not `recoveredSigner`.
     * @param  signer          The address of the some signer.
     * @param  recoveredSigner The address of the some recoveredSigner.
     * @return                 An error if `signer` is not `recoveredSigner`.
     */
    function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error) {
        return (signer == recoveredSigner) ? Error.NoError : Error.SignerMismatch;
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  Standard Signature Validation Method for Contracts via EIP-1271.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-1271: https://eips.ethereum.org/EIPS/eip-1271
 */
interface IERC1271 {
    /**
     * @dev    Returns a specific magic value if the provided signature is valid for the provided digest.
     * @param  digest     Hash of the data purported to have been signed.
     * @param  signature  Signature byte array associated with the digest.
     * @return magicValue Magic value 0x1626ba7e if the signature is valid.
     */
    function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4 magicValue);
}

Settings
{
  "remappings": [
    "@ensdomains/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/contracts-upgradeable/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "@uniswap/v4-core/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/",
    "common/=lib/evm-m-extensions/lib/common/src/",
    "ds-test/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "evm-m-extensions/=lib/evm-m-extensions/",
    "forge-gas-snapshot/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts-upgradeable/=lib/evm-m-extensions/lib/common/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/evm-m-extensions/lib/openzeppelin-foundry-upgrades/src/",
    "permit2/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/permit2/",
    "solmate/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/lib/solmate/",
    "uniswap-v4-periphery/=lib/evm-m-extensions/lib/uniswap-v4-periphery/",
    "v4-core/=lib/evm-m-extensions/lib/uniswap-v4-periphery/lib/v4-core/src/",
    "wrapped-m-token/=lib/evm-m-extensions/lib/wrapped-m-token/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "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":"swapFacility","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AccountFrozen","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AccountNotFrozen","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationAlreadyUsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"}],"name":"AuthorizationExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"}],"name":"AuthorizationNotYetValid","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"payee","type":"address"}],"name":"CallerMustBePayee","type":"error"},{"inputs":[],"name":"EarningIsDisabled","type":"error"},{"inputs":[],"name":"EarningIsEnabled","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureLength","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidSignatureV","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotSwapFacility","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"SignerMismatch","type":"error"},{"inputs":[],"name":"ZeroAdmin","type":"error"},{"inputs":[],"name":"ZeroForcedTransferManager","type":"error"},{"inputs":[],"name":"ZeroFreezeManager","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroPauser","type":"error"},{"inputs":[],"name":"ZeroSwapFacility","type":"error"},{"inputs":[],"name":"ZeroYieldRecipient","type":"error"},{"inputs":[],"name":"ZeroYieldRecipientManager","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"EarningDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"index","type":"uint128"}],"name":"EarningEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"frozenAccount","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"forcedTransferManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ForcedTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Frozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Unfrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"yield","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldRecipient","type":"address"}],"name":"YieldRecipientSet","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FORCED_TRANSFER_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FREEZE_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECEIVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"YIELD_RECIPIENT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields_","type":"bytes1"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"version_","type":"string"},{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"verifyingContract_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"uint256[]","name":"extensions_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"frozenAccount","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forceTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"frozenAccounts","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"forceTransfers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"freeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"freezeAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldRecipient","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"freezeManager","type":"address"},{"internalType":"address","name":"yieldRecipientManager","type":"address"},{"internalType":"address","name":"pauser","type":"address"},{"internalType":"address","name":"forcedTransferManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"yieldRecipient_","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"freezeManager","type":"address"},{"internalType":"address","name":"yieldRecipientManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isEarningEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setYieldRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapFacility","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unfreeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"unfreezeAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"wrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60c060405234801561001057600080fd5b50604051614feb380380614feb83398101604081905261002f9161016c565b8181818161003b61009e565b6001600160a01b03821660808190526100675760405163b01d5e2b60e01b815260040160405180910390fd5b6001600160a01b03811660a081905261009357604051636880ffc960e11b815260040160405180910390fd5b50505050505061019f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100ee5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161461014d5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b80516001600160a01b038116811461016757600080fd5b919050565b6000806040838503121561017f57600080fd5b61018883610150565b915061019660208401610150565b90509250929050565b60805160a051614de8610203600039600081816108b2015281816110c901526117cc01526000818161092401528181610ea901528181611674015281816118cd015281816119db015281816122140152818161269c015261303d0152614de86000f3fe608060405234801561001057600080fd5b50600436106103c55760003560e01c806388b7ab63116101ff578063c9144ddb1161011a578063d9169487116100ad578063e58398361161007c578063e583983614610aa4578063e63ab1e914610afc578063e94a010214610b23578063ef55bec614610b8857600080fd5b8063d9169487146109f2578063dd62ed3e14610a19578063e3ee160e14610a7e578063e56f2fe414610a9157600080fd5b8063cf092995116100e9578063cf092995146109a6578063d505accf146109b9578063d547741f146109cc578063d7a49f0b146109df57600080fd5b8063c9144ddb14610946578063c967891a1461094e578063cc2a9a5b14610956578063cc4c5b641461096957600080fd5b8063a8afc01f11610192578063ae06b7e411610161578063ae06b7e4146108ad578063b7b72899146108f9578063bf376c7a1461090c578063c3b6f9391461091f57600080fd5b8063a8afc01f1461086c578063a9059cbb14610874578063aad1202914610887578063ace150a51461089a57600080fd5b80639fd5a6cf116101ce5780639fd5a6cf14610817578063a08cb48b1461082a578063a0cc6a681461083d578063a217fddf1461086457600080fd5b806388b7ab63146107845780638d1fdf2f1461079757806391d14854146107aa57806395d89b411461080f57600080fd5b806336568abe116102ef5780635a049a70116102825780637ecebe00116102515780637ecebe00146106e55780637f2eecc31461073a5780638456cb591461076157806384b0196e1461076957600080fd5b80635a049a701461062c5780635c975abb1461063f57806363f156491461066957806370a082311461069057600080fd5b80634259dff9116102be5780634259dff9146105cc57806345c8b1a6146105f357806345cf012d14610606578063532992c51461061957600080fd5b806336568abe1461059657806339f47693146105a95780633f4ba83a146105bc578063406cf229146105c457600080fd5b806326987b601161036757806330adf81f1161033657806330adf81f14610520578063313ce5671461054757806333bebb771461057b5780633644e5151461058e57600080fd5b806326987b60146104b557806328593984146104de5780632cfd442d146104e65780632f2ff15d1461050d57600080fd5b8063170e2070116103a3578063170e20701461041a57806318160ddd1461042f57806323b872dd14610460578063248a9ca31461047357600080fd5b806301ffc9a7146103ca57806306fdde03146103f2578063095ea7b314610407575b600080fd5b6103dd6103d8366004614145565b610b9b565b60405190151581526020015b60405180910390f35b6103fa610c34565b6040516103e991906141f5565b6103dd61041536600461422a565b610ce9565b61042d61042836600461429b565b610cff565b005b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100545b6040519081526020016103e9565b6103dd61046e3660046142dd565b610d94565b61045261048136600461431e565b60009081527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015490565b6104bd610ea5565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e9565b610452610f3b565b6104527fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c81565b61042d61051b366004614337565b610f8e565b6104527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1015460405160ff90911681526020016103e9565b61042d6105893660046142dd565b610fd8565b61045261100d565b61042d6105a4366004614337565b611053565b61042d6105b736600461422a565b6110b1565b61042d61119c565b6104526111d1565b6104527f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca81565b61042d610601366004614367565b611276565b61042d610614366004614367565b6112ca565b61042d610627366004614384565b611306565b61042d61063a3660046143d0565b611325565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166103dd565b6104527f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3581565b61045261069e366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604090205490565b6104526106f3366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602052604090205490565b6104527fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b61042d611345565b610771611377565b6040516103e99796959493929190614420565b61042d6107923660046145c4565b6114d6565b61042d6107a5366004614367565b611505565b6103dd6107b8366004614337565b60009182527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103fa611559565b61042d61082536600461464d565b611581565b61042d6108383660046146c3565b611591565b6104527f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b610452600081565b61042d6115bb565b6103dd61088236600461422a565b6116e1565b61042d61089536600461429b565b6116ee565b61042d6108a83660046146c3565b61177c565b6108d47f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e9565b61042d61090736600461472e565b61179c565b61042d61091a36600461422a565b6117b4565b6108d47f000000000000000000000000000000000000000000000000000000000000000081565b6103dd61189c565b61042d61194d565b61042d610964366004614787565b611a41565b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff166108d4565b61042d6109b43660046145c4565b611d1a565b61042d6109c7366004614809565b611d3a565b61042d6109da366004614337565b611d4a565b61042d6109ed366004614878565b611d8e565b6104527f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b610452610a2736600461491e565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1006020908152604080832093909416825291909152205490565b61042d610a8c36600461494c565b611e80565b61042d610a9f3660046149d1565b611eab565b6103dd610ab2366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff1690565b6104527f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6103dd610b3136600461422a565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a488860060209081526040808320938352929052205460ff1690565b61042d610b9636600461494c565b612010565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c2e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1005b6002018054610c6690614a47565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9290614a47565b8015610cdf5780601f10610cb457610100808354040283529160200191610cdf565b820191906000526020600020905b815481529060010190602001808311610cc257829003601f168201915b5050505050905090565b6000610cf6338484612030565b50600192915050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b35610d2981612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d57610d8582868684818110610d6b57610d6b614a9a565b9050602002016020810190610d809190614367565b612050565b600101610d4d565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602081815260408084203385529091528220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e8e5783811015610e5a576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018590526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208381526040808320338452909152902084820390555b610e998686866120dc565b50600195945050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614ac9565b905090565b600080610f47306121cc565b90506000610f737fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1005490565b9050808211610f83576000610f87565b8082035b9250505090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154610fc881612046565b610fd28383612281565b50505050565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c61100281612046565b610fd28484846123a2565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1008054600091904614611047576110426124a6565b61104d565b80600101545b91505090565b73ffffffffffffffffffffffffffffffffffffffff811633146110a2576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac8282612562565b505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611120576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561116e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111929190614afb565b82612640565b5050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6111c681612046565b6111ce61271e565b50565b60006111db6127bb565b60006111e5610f3b565b9050806000036111f757600091505090565b6040518181527fd1c22369a95f91ae16576036bba6372736ba109f257ad94dccb89e141762e2659060200160405180910390a161127161126b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff1690565b826127e5565b919050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b356112a081612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612050565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6112f481612046565b6112fc6111d1565b5061119882612893565b61131b8461131486866129ba565b8484612a36565b610fd28484612a4a565b61133b8561133387876129ba565b858585612af8565b610d8d8585612a4a565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61136f81612046565b6111ce612b08565b6000606080828080837f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea10060020146306000806040519080825280602002602001820160405280156113d2578160200160208202803683370190505b507f0f00000000000000000000000000000000000000000000000000000000000000949392919084805461140590614a47565b80601f016020809104026020016040519081016040528092919081815260200182805461143190614a47565b801561147e5780601f106114535761010080835404028352916020019161147e565b820191906000526020600020905b81548152906001019060200180831161146157829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6114ee876114e8898989898989612b81565b83612c09565b6114fc878787878787612c56565b50505050505050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561152f81612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612cd9565b60607fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100610c58565b610d8d856114e887878787612d63565b6115a3886113148a8a8a8a8a8a612b81565b6115b1888888888888612c56565b5050505050505050565b6115c361189c565b6115f9576040517fb019ea3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce611622610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a16040517f81399be40000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906381399be490602401600060405180830381600087803b1580156116cd57600080fd5b505af1158015610fd2573d6000803e3d6000fd5b6000610cf63384846120dc565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561171881612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d576117748286868481811061175a5761175a614a9a565b905060200201602081019061176f9190614367565b612cd9565b60010161173c565b61178e886113148a8a8a8a8a8a612e2f565b6115b1888888888888612eac565b6117aa836114e885856129ba565b6110ac8383612a4a565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611823576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118959190614afb565b8383612fe5565b6040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906384af270f90602401602060405180830381865afa158015611929573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614b18565b61195561189c565b1561198c576040517f7a42736800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a186119b5610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a17f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a36e40fc6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116cd57600080fd5b6000611a4b6130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611a785750825b905060008267ffffffffffffffff166001148015611a955750303b155b905081158015611aa3575080155b15611ada576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611b3b5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b73ffffffffffffffffffffffffffffffffffffffff8716611b88576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616611bd5576040517f354368a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c4d6040518060400160405280600c81526020017f4d6574614d61736b2055534400000000000000000000000000000000000000008152506040518060400160405280600481526020017f6d555344000000000000000000000000000000000000000000000000000000008152508d8d8d8d6130f3565b611c556131e7565b611c7f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a88612281565b50611caa7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c87612281565b508315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020015b60405180910390a15b5050505050505050505050565b611d2c876114e8898989898989612e2f565b6114fc878787878787612eac565b6114fc8761133389898989612d63565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154611d8481612046565b610fd28383612562565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c611db881612046565b8584141580611dc75750858214155b15611dfe576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b868110156115b157611e78888883818110611e1e57611e1e614a9a565b9050602002016020810190611e339190614367565b878784818110611e4557611e45614a9a565b9050602002016020810190611e5a9190614367565b868685818110611e6c57611e6c614a9a565b905060200201356123a2565b600101611e01565b611e92896113338b8b8b8b8b8b612e2f565b611ea0898989898989612eac565b505050505050505050565b6000611eb56130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611ee25750825b905060008267ffffffffffffffff166001148015611eff5750303b155b905081158015611f0d575080155b15611f44576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611fa55784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b611fb38b8b8b8b8b8b6130f3565b8315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611d04565b612022896113338b8b8b8b8b8b612b81565b611ea0898989898989612c56565b61203b8383836131f1565b6110ac838383613226565b6111ce81336132ab565b61205a8282613352565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590514281527f07d647ad688e085159820c1d8d030e5765cdc5274d4ee4065c6066b388a2ef5991015b60405180910390a25050565b6120e5826133c9565b6120f083838361342e565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161214f91815260200190565b60405180910390a38060000361216457505050565b61216e8382613441565b73ffffffffffffffffffffffffffffffffffffffff80841660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604080822080548590039055918416815220805482019055505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa15801561225d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2e9190614b3a565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff166123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556123343390565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610c2e565b6000915050610c2e565b6123ab826133c9565b6123b4836134e6565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161241391815260200190565b60405180910390a33373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f47cea260e2dfb95ed2ab59ad44fe2ac9cddb432afb828d2a1475936b5a2b829a8460405161249191815260200190565b60405180910390a48060000361216457505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1006002016040516124fa9190614b53565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff16156123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610c2e565b6126498161357c565b61265382826135b9565b61265d3382613441565b61266733826135cb565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156126fa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ac9190614b18565b612726613671565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6111ce81612046565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602090815260408083208054860190557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054860181559051858152909392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff81166128e0576040517f5dfd400c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af101547fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1009073ffffffffffffffffffffffffffffffffffffffff90811690831603612948575050565b6001810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517f77f12a3c9f87d4602fe59bb8d2b68c7b516e0cacba414a53e74ea75d435dc18d90600090a25050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff84169181019190915260608101829052600090612a2f906080015b604051602081830303815290604052805190602001206136cc565b9392505050565b610fd2612a458585858561372e565b61376c565b612a548282613931565b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610d8d612a4586868686866139da565b612b10613a25565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612790565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b979650505050505050565b6000612c16848484613a81565b90506000816005811115612c2c57612c2c614be6565b03612c375750505050565b612c42848484613ac8565b15612c4d5750505050565b610fd28161376c565b3373ffffffffffffffffffffffffffffffffffffffff861614612cc3576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff86166024820152604401610e51565b612cd1868686868686612eac565b505050505050565b612ce38282613c15565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f68e0d8c112165d0949ce87205b719ed7d98c7401866c34a159f7c67c6f5620e7906120d09042815260200190565b6000612d6e82613c8d565b612d79858585612030565b73ffffffffffffffffffffffffffffffffffffffff80861660008181527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052612e269060e001612a14565b95945050505050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b824211612eee576040517f498ff9a200000000000000000000000000000000000000000000000000000000815242600482015260248101849052604401610e51565b814210612f30576040517fb3fcd33e00000000000000000000000000000000000000000000000000000000815242600482015260248101839052604401610e51565b612f3a8682613931565b73ffffffffffffffffffffffffffffffffffffffff861660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3612cd18686866120dc565b612fee826133c9565b612ff78161357c565b613002838383613cd0565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af115801561309b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bf9190614b18565b506110ac82826127e5565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c2e565b6130fb613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613148576040517f09d50edf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316613195576040517f7289db0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f8686613d21565b6131a882613d35565b6131b184612893565b6131bc600084612281565b506114fc7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca82612281565b6131ef613ce3565b565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0061321c8185613c15565b610fd28184613c15565b73ffffffffffffffffffffffffffffffffffffffff83811660008181527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101612886565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16611198576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff16611198576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b73ffffffffffffffffffffffffffffffffffffffff81166111ce576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b613436613a25565b6110ac838383613db4565b73ffffffffffffffffffffffffffffffffffffffff821660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1026020526040902054818110156110ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024810182905260448101839052606401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff166111ce576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b806000036111ce576040517f77b8dde300000000000000000000000000000000000000000000000000000000815260048101829052602401610e51565b6135c1613a25565b6111988282613de9565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af10260209081526040808320805486900390557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054869003815590518581529093917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101612886565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166131ef576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006136d661100d565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b600080600061373e868686613e13565b9092509050600082600581111561375757613757614be6565b146137625781612bfe565b612bfe8782613e5b565b600081600581111561378057613780614be6565b036137885750565b600181600581111561379c5761379c614be6565b036137d3576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028160058111156137e7576137e7614be6565b0361381e576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381600581111561383257613832614be6565b03613869576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561387d5761387d614be6565b036138b4576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058160058111156138c8576138c8614be6565b036138ff576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832084845290915290205460ff1615611198576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101829052604401610e51565b60008060006139eb87878787613ea0565b90925090506000826005811115613a0457613a04614be6565b14613a0f5781613a19565b613a198882613e5b565b98975050505050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16156131ef576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000613a908585613fb5565b90925090506000826005811115613aa957613aa9614be6565b14613ab45781613abe565b613abe8682613e5b565b9695505050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff168585604051602401613af7929190614c15565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251613b789190614c36565b600060405180830381855afa9150503d8060008114613bb3576040519150601f19603f3d011682016040523d82523d6000602084013e613bb8565b606091505b5091509150818015613bcc57506020815110155b8015613abe575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090613c0a9083016020908101908401614b3a565b149695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff1615611198576040517f4f2a367e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b804211156111ce576040517ff88f049000000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610e51565b613cd8613a25565b6110ac8383836131f1565b613ceb613ffc565b6131ef576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613d29613ce3565b6111988282600661401b565b613d3d613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613d8a576040517f6cbbd1d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111987f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3582612281565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00613ddf8133613c15565b61321c8185613c15565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083613c15565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416613e4d87838884613ea0565b935093505050935093915050565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613e97576005612a2f565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613ed75750600390506000613fac565b8460ff16601b14158015613eef57508460ff16601c14155b15613f005750600490506000613fac565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613f53573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615613fa257600081613fa7565b600160005b915091505b94509492505050565b6000808251604114613fcd5750600290506000613ff5565b60208301516040840151606085015160001a9190613fed87848484613ea0565b945094505050505b9250929050565b60006140066130ca565b5468010000000000000000900460ff16919050565b614023613ce3565b61402c836140c4565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da10180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83161790557fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1007fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da102610d8d8482614c99565b6140cc613ce3565b6111ce816140d8613ce3565b6111ce816140e4613ce3565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1007f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1026141308382614c99565b5046815561413c6124a6565b60019091015550565b60006020828403121561415757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2f57600080fd5b60005b838110156141a257818101518382015260200161418a565b50506000910152565b600081518084526141c3816020860160208601614187565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612a2f60208301846141ab565b73ffffffffffffffffffffffffffffffffffffffff811681146111ce57600080fd5b6000806040838503121561423d57600080fd5b823561424881614208565b946020939093013593505050565b60008083601f84011261426857600080fd5b50813567ffffffffffffffff81111561428057600080fd5b6020830191508360208260051b8501011115613ff557600080fd5b600080602083850312156142ae57600080fd5b823567ffffffffffffffff8111156142c557600080fd5b6142d185828601614256565b90969095509350505050565b6000806000606084860312156142f257600080fd5b83356142fd81614208565b9250602084013561430d81614208565b929592945050506040919091013590565b60006020828403121561433057600080fd5b5035919050565b6000806040838503121561434a57600080fd5b82359150602083013561435c81614208565b809150509250929050565b60006020828403121561437957600080fd5b8135612a2f81614208565b6000806000806080858703121561439a57600080fd5b84356143a581614208565b966020860135965060408601359560600135945092505050565b803560ff8116811461127157600080fd5b600080600080600060a086880312156143e857600080fd5b85356143f381614208565b945060208601359350614408604087016143bf565b94979396509394606081013594506080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061445b60e08301896141ab565b828103604084015261446d81896141ab565b6060840188905273ffffffffffffffffffffffffffffffffffffffff8716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b818110156144d05783518352602093840193909201916001016144b2565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261452157600080fd5b81356020830160008067ffffffffffffffff841115614542576145426144e1565b506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85018116603f0116810181811067ffffffffffffffff8211171561458f5761458f6144e1565b6040528381529050808284018710156145a757600080fd5b838360208301376000602085830101528094505050505092915050565b600080600080600080600060e0888a0312156145df57600080fd5b87356145ea81614208565b965060208801356145fa81614208565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561463257600080fd5b61463e8a828b01614510565b91505092959891949750929550565b600080600080600060a0868803121561466557600080fd5b853561467081614208565b9450602086013561468081614208565b93506040860135925060608601359150608086013567ffffffffffffffff8111156146aa57600080fd5b6146b688828901614510565b9150509295509295909350565b600080600080600080600080610100898b0312156146e057600080fd5b88356146eb81614208565b975060208901356146fb81614208565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060006060848603121561474357600080fd5b833561474e81614208565b925060208401359150604084013567ffffffffffffffff81111561477157600080fd5b61477d86828701614510565b9150509250925092565b60008060008060008060c087890312156147a057600080fd5b86356147ab81614208565b955060208701356147bb81614208565b945060408701356147cb81614208565b935060608701356147db81614208565b925060808701356147eb81614208565b915060a08701356147fb81614208565b809150509295509295509295565b600080600080600080600060e0888a03121561482457600080fd5b873561482f81614208565b9650602088013561483f81614208565b9550604088013594506060880135935061485b608089016143bf565b9699959850939692959460a0840135945060c09093013592915050565b6000806000806000806060878903121561489157600080fd5b863567ffffffffffffffff8111156148a857600080fd5b6148b489828a01614256565b909750955050602087013567ffffffffffffffff8111156148d457600080fd5b6148e089828a01614256565b909550935050604087013567ffffffffffffffff81111561490057600080fd5b61490c89828a01614256565b979a9699509497509295939492505050565b6000806040838503121561493157600080fd5b823561493c81614208565b9150602083013561435c81614208565b60008060008060008060008060006101208a8c03121561496b57600080fd5b893561497681614208565b985060208a013561498681614208565b975060408a0135965060608a0135955060808a0135945060a08a013593506149b060c08b016143bf565b989b979a50959894979396929550929360e081013593506101000135919050565b60008060008060008060c087890312156149ea57600080fd5b863567ffffffffffffffff811115614a0157600080fd5b614a0d89828a01614510565b965050602087013567ffffffffffffffff811115614a2a57600080fd5b614a3689828a01614510565b95505060408701356147cb81614208565b600181811c90821680614a5b57607f821691505b602082108103614a94577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614adb57600080fd5b81516fffffffffffffffffffffffffffffffff81168114612a2f57600080fd5b600060208284031215614b0d57600080fd5b8151612a2f81614208565b600060208284031215614b2a57600080fd5b81518015158114612a2f57600080fd5b600060208284031215614b4c57600080fd5b5051919050565b6000808354614b6181614a47565b600182168015614b785760018114614bab57614bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083168652811515820286019350614bdb565b86600052602060002060005b83811015614bd357815488820152600190910190602001614bb7565b505081860193505b509195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000614c2e60408301846141ab565b949350505050565b60008251614c48818460208701614187565b9190910192915050565b601f8211156110ac57806000526020600020601f840160051c81016020851015614c795750805b601f840160051c820191505b81811015610d8d5760008155600101614c85565b815167ffffffffffffffff811115614cb357614cb36144e1565b614cc781614cc18454614a47565b84614c52565b6020601f821160018114614d195760008315614ce35750848201515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178455610d8d565b6000848152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08516915b82811015614d675787850151825560209485019460019092019101614d47565b5084821015614da357868401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b60f8161c191681555b50505050600190811b0190555056fea26469706673582212207ed056e91645947340e29d61720bf8d2aa1bfe6e139bd66c9e46b7c2b88cfada64736f6c634300081a0033000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103c55760003560e01c806388b7ab63116101ff578063c9144ddb1161011a578063d9169487116100ad578063e58398361161007c578063e583983614610aa4578063e63ab1e914610afc578063e94a010214610b23578063ef55bec614610b8857600080fd5b8063d9169487146109f2578063dd62ed3e14610a19578063e3ee160e14610a7e578063e56f2fe414610a9157600080fd5b8063cf092995116100e9578063cf092995146109a6578063d505accf146109b9578063d547741f146109cc578063d7a49f0b146109df57600080fd5b8063c9144ddb14610946578063c967891a1461094e578063cc2a9a5b14610956578063cc4c5b641461096957600080fd5b8063a8afc01f11610192578063ae06b7e411610161578063ae06b7e4146108ad578063b7b72899146108f9578063bf376c7a1461090c578063c3b6f9391461091f57600080fd5b8063a8afc01f1461086c578063a9059cbb14610874578063aad1202914610887578063ace150a51461089a57600080fd5b80639fd5a6cf116101ce5780639fd5a6cf14610817578063a08cb48b1461082a578063a0cc6a681461083d578063a217fddf1461086457600080fd5b806388b7ab63146107845780638d1fdf2f1461079757806391d14854146107aa57806395d89b411461080f57600080fd5b806336568abe116102ef5780635a049a70116102825780637ecebe00116102515780637ecebe00146106e55780637f2eecc31461073a5780638456cb591461076157806384b0196e1461076957600080fd5b80635a049a701461062c5780635c975abb1461063f57806363f156491461066957806370a082311461069057600080fd5b80634259dff9116102be5780634259dff9146105cc57806345c8b1a6146105f357806345cf012d14610606578063532992c51461061957600080fd5b806336568abe1461059657806339f47693146105a95780633f4ba83a146105bc578063406cf229146105c457600080fd5b806326987b601161036757806330adf81f1161033657806330adf81f14610520578063313ce5671461054757806333bebb771461057b5780633644e5151461058e57600080fd5b806326987b60146104b557806328593984146104de5780632cfd442d146104e65780632f2ff15d1461050d57600080fd5b8063170e2070116103a3578063170e20701461041a57806318160ddd1461042f57806323b872dd14610460578063248a9ca31461047357600080fd5b806301ffc9a7146103ca57806306fdde03146103f2578063095ea7b314610407575b600080fd5b6103dd6103d8366004614145565b610b9b565b60405190151581526020015b60405180910390f35b6103fa610c34565b6040516103e991906141f5565b6103dd61041536600461422a565b610ce9565b61042d61042836600461429b565b610cff565b005b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af100545b6040519081526020016103e9565b6103dd61046e3660046142dd565b610d94565b61045261048136600461431e565b60009081527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015490565b6104bd610ea5565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e9565b610452610f3b565b6104527fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c81565b61042d61051b366004614337565b610f8e565b6104527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1015460405160ff90911681526020016103e9565b61042d6105893660046142dd565b610fd8565b61045261100d565b61042d6105a4366004614337565b611053565b61042d6105b736600461422a565b6110b1565b61042d61119c565b6104526111d1565b6104527f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca81565b61042d610601366004614367565b611276565b61042d610614366004614367565b6112ca565b61042d610627366004614384565b611306565b61042d61063a3660046143d0565b611325565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166103dd565b6104527f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3581565b61045261069e366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604090205490565b6104526106f3366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602052604090205490565b6104527fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b61042d611345565b610771611377565b6040516103e99796959493929190614420565b61042d6107923660046145c4565b6114d6565b61042d6107a5366004614367565b611505565b6103dd6107b8366004614337565b60009182527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103fa611559565b61042d61082536600461464d565b611581565b61042d6108383660046146c3565b611591565b6104527f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b610452600081565b61042d6115bb565b6103dd61088236600461422a565b6116e1565b61042d61089536600461429b565b6116ee565b61042d6108a83660046146c3565b61177c565b6108d47f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f627881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e9565b61042d61090736600461472e565b61179c565b61042d61091a36600461422a565b6117b4565b6108d47f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b81565b6103dd61189c565b61042d61194d565b61042d610964366004614787565b611a41565b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff166108d4565b61042d6109b43660046145c4565b611d1a565b61042d6109c7366004614809565b611d3a565b61042d6109da366004614337565b611d4a565b61042d6109ed366004614878565b611d8e565b6104527f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b610452610a2736600461491e565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1006020908152604080832093909416825291909152205490565b61042d610a8c36600461494c565b611e80565b61042d610a9f3660046149d1565b611eab565b6103dd610ab2366004614367565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff1690565b6104527f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6103dd610b3136600461422a565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a488860060209081526040808320938352929052205460ff1690565b61042d610b9636600461494c565b612010565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c2e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1005b6002018054610c6690614a47565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9290614a47565b8015610cdf5780601f10610cb457610100808354040283529160200191610cdf565b820191906000526020600020905b815481529060010190602001808311610cc257829003601f168201915b5050505050905090565b6000610cf6338484612030565b50600192915050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b35610d2981612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d57610d8582868684818110610d6b57610d6b614a9a565b9050602002016020810190610d809190614367565b612050565b600101610d4d565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602081815260408084203385529091528220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e8e5783811015610e5a576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018590526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208381526040808320338452909152902084820390555b610e998686866120dc565b50600195945050505050565b60007f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff166326987b606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614ac9565b905090565b600080610f47306121cc565b90506000610f737fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1005490565b9050808211610f83576000610f87565b8082035b9250505090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154610fc881612046565b610fd28383612281565b50505050565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c61100281612046565b610fd28484846123a2565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1008054600091904614611047576110426124a6565b61104d565b80600101545b91505090565b73ffffffffffffffffffffffffffffffffffffffff811633146110a2576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac8282612562565b505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f62781614611120576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561116e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111929190614afb565b82612640565b5050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6111c681612046565b6111ce61271e565b50565b60006111db6127bb565b60006111e5610f3b565b9050806000036111f757600091505090565b6040518181527fd1c22369a95f91ae16576036bba6372736ba109f257ad94dccb89e141762e2659060200160405180910390a161127161126b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1015473ffffffffffffffffffffffffffffffffffffffff1690565b826127e5565b919050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b356112a081612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612050565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6112f481612046565b6112fc6111d1565b5061119882612893565b61131b8461131486866129ba565b8484612a36565b610fd28484612a4a565b61133b8561133387876129ba565b858585612af8565b610d8d8585612a4a565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61136f81612046565b6111ce612b08565b6000606080828080837f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea10060020146306000806040519080825280602002602001820160405280156113d2578160200160208202803683370190505b507f0f00000000000000000000000000000000000000000000000000000000000000949392919084805461140590614a47565b80601f016020809104026020016040519081016040528092919081815260200182805461143190614a47565b801561147e5780601f106114535761010080835404028352916020019161147e565b820191906000526020600020905b81548152906001019060200180831161146157829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6114ee876114e8898989898989612b81565b83612c09565b6114fc878787878787612c56565b50505050505050565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561152f81612046565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083612cd9565b60607fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100610c58565b610d8d856114e887878787612d63565b6115a3886113148a8a8a8a8a8a612b81565b6115b1888888888888612c56565b5050505050505050565b6115c361189c565b6115f9576040517fb019ea3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce611622610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a16040517f81399be40000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906381399be490602401600060405180830381600087803b1580156116cd57600080fd5b505af1158015610fd2573d6000803e3d6000fd5b6000610cf63384846120dc565b7f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3561171881612046565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0060005b83811015610d8d576117748286868481811061175a5761175a614a9a565b905060200201602081019061176f9190614367565b612cd9565b60010161173c565b61178e886113148a8a8a8a8a8a612e2f565b6115b1888888888888612eac565b6117aa836114e885856129ba565b6110ac8383612a4a565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f62781614611823576040517f2bfe1b5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111983373ffffffffffffffffffffffffffffffffffffffff1663d737d0c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118959190614afb565b8383612fe5565b6040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906384af270f90602401602060405180830381865afa158015611929573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f369190614b18565b61195561189c565b1561198c576040517f7a42736800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a186119b5610ea5565b6040516fffffffffffffffffffffffffffffffff909116815260200160405180910390a17f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff1663a36e40fc6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116cd57600080fd5b6000611a4b6130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611a785750825b905060008267ffffffffffffffff166001148015611a955750303b155b905081158015611aa3575080155b15611ada576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611b3b5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b73ffffffffffffffffffffffffffffffffffffffff8716611b88576040517f042d717b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616611bd5576040517f354368a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c4d6040518060400160405280600c81526020017f4d6574614d61736b2055534400000000000000000000000000000000000000008152506040518060400160405280600481526020017f6d555344000000000000000000000000000000000000000000000000000000008152508d8d8d8d6130f3565b611c556131e7565b611c7f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a88612281565b50611caa7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c87612281565b508315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020015b60405180910390a15b5050505050505050505050565b611d2c876114e8898989898989612e2f565b6114fc878787878787612eac565b6114fc8761133389898989612d63565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154611d8481612046565b610fd28383612562565b7fc66b3536568140ce119bcc21a4fa7e3449a56fb5f260d32ff8e719230264132c611db881612046565b8584141580611dc75750858214155b15611dfe576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b868110156115b157611e78888883818110611e1e57611e1e614a9a565b9050602002016020810190611e339190614367565b878784818110611e4557611e45614a9a565b9050602002016020810190611e5a9190614367565b868685818110611e6c57611e6c614a9a565b905060200201356123a2565b600101611e01565b611e92896113338b8b8b8b8b8b612e2f565b611ea0898989898989612eac565b505050505050505050565b6000611eb56130ca565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015611ee25750825b905060008267ffffffffffffffff166001148015611eff5750303b155b905081158015611f0d575080155b15611f44576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315611fa55784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b611fb38b8b8b8b8b8b6130f3565b8315611d0d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611d04565b612022896113338b8b8b8b8b8b612b81565b611ea0898989898989612c56565b61203b8383836131f1565b6110ac838383613226565b6111ce81336132ab565b61205a8282613352565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590514281527f07d647ad688e085159820c1d8d030e5765cdc5274d4ee4065c6066b388a2ef5991015b60405180910390a25050565b6120e5826133c9565b6120f083838361342e565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161214f91815260200190565b60405180910390a38060000361216457505050565b61216e8382613441565b73ffffffffffffffffffffffffffffffffffffffff80841660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602052604080822080548590039055918416815220805482019055505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b909116906370a0823190602401602060405180830381865afa15801561225d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2e9190614b3a565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff166123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556123343390565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610c2e565b6000915050610c2e565b6123ab826133c9565b6123b4836134e6565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161241391815260200190565b60405180910390a33373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f47cea260e2dfb95ed2ab59ad44fe2ac9cddb432afb828d2a1475936b5a2b829a8460405161249191815260200190565b60405180910390a48060000361216457505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1006002016040516124fa9190614b53565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff16156123985760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610c2e565b6126498161357c565b61265382826135b9565b61265d3382613441565b61266733826135cb565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018290527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303816000875af11580156126fa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ac9190614b18565b612726613671565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca6111ce81612046565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af102602090815260408083208054860190557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054860181559051858152909392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff81166128e0576040517f5dfd400c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af101547fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1009073ffffffffffffffffffffffffffffffffffffffff90811690831603612948575050565b6001810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517f77f12a3c9f87d4602fe59bb8d2b68c7b516e0cacba414a53e74ea75d435dc18d90600090a25050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff84169181019190915260608101829052600090612a2f906080015b604051602081830303815290604052805190602001206136cc565b9392505050565b610fd2612a458585858561372e565b61376c565b612a548282613931565b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610d8d612a4586868686866139da565b612b10613a25565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f0330080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612790565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b979650505050505050565b6000612c16848484613a81565b90506000816005811115612c2c57612c2c614be6565b03612c375750505050565b612c42848484613ac8565b15612c4d5750505050565b610fd28161376c565b3373ffffffffffffffffffffffffffffffffffffffff861614612cc3576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff86166024820152604401610e51565b612cd1868686868686612eac565b505050505050565b612ce38282613c15565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602084905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f68e0d8c112165d0949ce87205b719ed7d98c7401866c34a159f7c67c6f5620e7906120d09042815260200190565b6000612d6e82613c8d565b612d79858585612030565b73ffffffffffffffffffffffffffffffffffffffff80861660008181527f1b21ba3f0a2135d61c468900b54084f04af8111bce0f8bbb6ab8c46d11afbd00602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052612e269060e001612a14565b95945050505050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090612bfe9061010001612a14565b824211612eee576040517f498ff9a200000000000000000000000000000000000000000000000000000000815242600482015260248101849052604401610e51565b814210612f30576040517fb3fcd33e00000000000000000000000000000000000000000000000000000000815242600482015260248101839052604401610e51565b612f3a8682613931565b73ffffffffffffffffffffffffffffffffffffffff861660008181527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3612cd18686866120dc565b612fee826133c9565b612ff78161357c565b613002838383613cd0565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018290527f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b73ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af115801561309b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bf9190614b18565b506110ac82826127e5565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c2e565b6130fb613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613148576040517f09d50edf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316613195576040517f7289db0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f8686613d21565b6131a882613d35565b6131b184612893565b6131bc600084612281565b506114fc7f4a5e9eb1ba56d04185ff75ebf0f4f42a3d7c88c35b4a90fa278437e0c9bdceca82612281565b6131ef613ce3565b565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0061321c8185613c15565b610fd28184613c15565b73ffffffffffffffffffffffffffffffffffffffff83811660008181527fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da100602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101612886565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16611198576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff16611198576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b73ffffffffffffffffffffffffffffffffffffffff81166111ce576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b613436613a25565b6110ac838383613db4565b73ffffffffffffffffffffffffffffffffffffffff821660009081527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1026020526040902054818110156110ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024810182905260448101839052606401610e51565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00602052604090205460ff166111ce576040517ffc78247900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b806000036111ce576040517f77b8dde300000000000000000000000000000000000000000000000000000000815260048101829052602401610e51565b6135c1613a25565b6111988282613de9565b73ffffffffffffffffffffffffffffffffffffffff821660008181527fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af10260209081526040808320805486900390557fee2f6fc7e2e5879b17985791e0d12536cba689bda43c77b8911497248f4af1008054869003815590518581529093917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101612886565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff166131ef576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006136d661100d565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b600080600061373e868686613e13565b9092509050600082600581111561375757613757614be6565b146137625781612bfe565b612bfe8782613e5b565b600081600581111561378057613780614be6565b036137885750565b600181600581111561379c5761379c614be6565b036137d3576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028160058111156137e7576137e7614be6565b0361381e576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381600581111561383257613832614be6565b03613869576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561387d5761387d614be6565b036138b4576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058160058111156138c8576138c8614be6565b036138ff576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1116a1d33aa5fb91b2652b3b0fdb63704173742d6dbecaf4256ebe33a48886006020908152604080832084845290915290205460ff1615611198576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101829052604401610e51565b60008060006139eb87878787613ea0565b90925090506000826005811115613a0457613a04614be6565b14613a0f5781613a19565b613a198882613e5b565b98975050505050505050565b7fcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033005460ff16156131ef576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000613a908585613fb5565b90925090506000826005811115613aa957613aa9614be6565b14613ab45781613abe565b613abe8682613e5b565b9695505050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff168585604051602401613af7929190614c15565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251613b789190614c36565b600060405180830381855afa9150503d8060008114613bb3576040519150601f19603f3d011682016040523d82523d6000602084013e613bb8565b606091505b5091509150818015613bcc57506020815110155b8015613abe575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090613c0a9083016020908101908401614b3a565b149695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526020839052604090205460ff1615611198576040517f4f2a367e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610e51565b804211156111ce576040517ff88f049000000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610e51565b613cd8613a25565b6110ac8383836131f1565b613ceb613ffc565b6131ef576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613d29613ce3565b6111988282600661401b565b613d3d613ce3565b73ffffffffffffffffffffffffffffffffffffffff8116613d8a576040517f6cbbd1d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111987f109b88c1c8d528799ca6f455418979dd2a552493f14553ce44443b23f7df8b3582612281565b7f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce00613ddf8133613c15565b61321c8185613c15565b6111987f2fd5767309dce890c526ace85d7fe164825199d7dcd99c33588befc51b32ce0083613c15565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416613e4d87838884613ea0565b935093505050935093915050565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613e97576005612a2f565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613ed75750600390506000613fac565b8460ff16601b14158015613eef57508460ff16601c14155b15613f005750600490506000613fac565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613f53573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615613fa257600081613fa7565b600160005b915091505b94509492505050565b6000808251604114613fcd5750600290506000613ff5565b60208301516040840151606085015160001a9190613fed87848484613ea0565b945094505050505b9250929050565b60006140066130ca565b5468010000000000000000900460ff16919050565b614023613ce3565b61402c836140c4565b7fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da10180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83161790557fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da1007fcbbe23efb65c1eaba394256c463812c20abdb5376e247eba1d0e1e92054da102610d8d8482614c99565b6140cc613ce3565b6111ce816140d8613ce3565b6111ce816140e4613ce3565b7f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1007f103ce0bed7138196cdb0d79ef04042681b16e7a2c58d74b78443c813042ea1026141308382614c99565b5046815561413c6124a6565b60019091015550565b60006020828403121561415757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2f57600080fd5b60005b838110156141a257818101518382015260200161418a565b50506000910152565b600081518084526141c3816020860160208601614187565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612a2f60208301846141ab565b73ffffffffffffffffffffffffffffffffffffffff811681146111ce57600080fd5b6000806040838503121561423d57600080fd5b823561424881614208565b946020939093013593505050565b60008083601f84011261426857600080fd5b50813567ffffffffffffffff81111561428057600080fd5b6020830191508360208260051b8501011115613ff557600080fd5b600080602083850312156142ae57600080fd5b823567ffffffffffffffff8111156142c557600080fd5b6142d185828601614256565b90969095509350505050565b6000806000606084860312156142f257600080fd5b83356142fd81614208565b9250602084013561430d81614208565b929592945050506040919091013590565b60006020828403121561433057600080fd5b5035919050565b6000806040838503121561434a57600080fd5b82359150602083013561435c81614208565b809150509250929050565b60006020828403121561437957600080fd5b8135612a2f81614208565b6000806000806080858703121561439a57600080fd5b84356143a581614208565b966020860135965060408601359560600135945092505050565b803560ff8116811461127157600080fd5b600080600080600060a086880312156143e857600080fd5b85356143f381614208565b945060208601359350614408604087016143bf565b94979396509394606081013594506080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061445b60e08301896141ab565b828103604084015261446d81896141ab565b6060840188905273ffffffffffffffffffffffffffffffffffffffff8716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b818110156144d05783518352602093840193909201916001016144b2565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261452157600080fd5b81356020830160008067ffffffffffffffff841115614542576145426144e1565b506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85018116603f0116810181811067ffffffffffffffff8211171561458f5761458f6144e1565b6040528381529050808284018710156145a757600080fd5b838360208301376000602085830101528094505050505092915050565b600080600080600080600060e0888a0312156145df57600080fd5b87356145ea81614208565b965060208801356145fa81614208565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561463257600080fd5b61463e8a828b01614510565b91505092959891949750929550565b600080600080600060a0868803121561466557600080fd5b853561467081614208565b9450602086013561468081614208565b93506040860135925060608601359150608086013567ffffffffffffffff8111156146aa57600080fd5b6146b688828901614510565b9150509295509295909350565b600080600080600080600080610100898b0312156146e057600080fd5b88356146eb81614208565b975060208901356146fb81614208565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060006060848603121561474357600080fd5b833561474e81614208565b925060208401359150604084013567ffffffffffffffff81111561477157600080fd5b61477d86828701614510565b9150509250925092565b60008060008060008060c087890312156147a057600080fd5b86356147ab81614208565b955060208701356147bb81614208565b945060408701356147cb81614208565b935060608701356147db81614208565b925060808701356147eb81614208565b915060a08701356147fb81614208565b809150509295509295509295565b600080600080600080600060e0888a03121561482457600080fd5b873561482f81614208565b9650602088013561483f81614208565b9550604088013594506060880135935061485b608089016143bf565b9699959850939692959460a0840135945060c09093013592915050565b6000806000806000806060878903121561489157600080fd5b863567ffffffffffffffff8111156148a857600080fd5b6148b489828a01614256565b909750955050602087013567ffffffffffffffff8111156148d457600080fd5b6148e089828a01614256565b909550935050604087013567ffffffffffffffff81111561490057600080fd5b61490c89828a01614256565b979a9699509497509295939492505050565b6000806040838503121561493157600080fd5b823561493c81614208565b9150602083013561435c81614208565b60008060008060008060008060006101208a8c03121561496b57600080fd5b893561497681614208565b985060208a013561498681614208565b975060408a0135965060608a0135955060808a0135945060a08a013593506149b060c08b016143bf565b989b979a50959894979396929550929360e081013593506101000135919050565b60008060008060008060c087890312156149ea57600080fd5b863567ffffffffffffffff811115614a0157600080fd5b614a0d89828a01614510565b965050602087013567ffffffffffffffff811115614a2a57600080fd5b614a3689828a01614510565b95505060408701356147cb81614208565b600181811c90821680614a5b57607f821691505b602082108103614a94577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614adb57600080fd5b81516fffffffffffffffffffffffffffffffff81168114612a2f57600080fd5b600060208284031215614b0d57600080fd5b8151612a2f81614208565b600060208284031215614b2a57600080fd5b81518015158114612a2f57600080fd5b600060208284031215614b4c57600080fd5b5051919050565b6000808354614b6181614a47565b600182168015614b785760018114614bab57614bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083168652811515820286019350614bdb565b86600052602060002060005b83811015614bd357815488820152600190910190602001614bb7565b505081860193505b509195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000614c2e60408301846141ab565b949350505050565b60008251614c48818460208701614187565b9190910192915050565b601f8211156110ac57806000526020600020601f840160051c81016020851015614c795750805b601f840160051c820191505b81811015610d8d5760008155600101614c85565b815167ffffffffffffffff811115614cb357614cb36144e1565b614cc781614cc18454614a47565b84614c52565b6020601f821160018114614d195760008315614ce35750848201515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178455610d8d565b6000848152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08516915b82811015614d675787850151825560209485019460019092019101614d47565b5084821015614da357868401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b60f8161c191681555b50505050600190811b0190555056fea26469706673582212207ed056e91645947340e29d61720bf8d2aa1bfe6e139bd66c9e46b7c2b88cfada64736f6c634300081a0033

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

000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000b6807116b3b1b321a390594e31ecd6e0076f6278

-----Decoded View---------------
Arg [0] : mToken (address): 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b
Arg [1] : swapFacility (address): 0xB6807116b3B1B321a390594e31ECD6e0076f6278

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b
Arg [1] : 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.