ETH Price: $2,949.42 (+0.29%)

Contract

0x813B926B1D096e117721bD1Eb017FbA122302DA0

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

ContractCreator

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WrappedMToken

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 1500 runs

Other Settings:
cancun EvmVersion
File 1 of 21 : WrappedMToken.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

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

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

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

import { IMTokenLike } from "./interfaces/IMTokenLike.sol";
import { IRegistrarLike } from "./interfaces/IRegistrarLike.sol";
import { IWrappedMToken } from "./interfaces/IWrappedMToken.sol";

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

/*

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

*/

/**
 * @title  ERC20 Token contract for wrapping M into a non-rebasing token with claimable yields.
 * @author M^0 Labs
 */
contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
    struct Account {
        bool isEarning;
        uint240 balance;
        uint128 lastIndex;
    }

    /* ============ Variables ============ */

    /// @dev Registrar key holding value of whether the earners list can be ignored or not.
    bytes32 internal constant _EARNERS_LIST_IGNORED = "earners_list_ignored";

    /// @dev Registrar key of earners list.
    bytes32 internal constant _EARNERS_LIST = "earners";

    /// @dev Registrar key prefix to determine the override recipient of an account's accrued yield.
    bytes32 internal constant _CLAIM_OVERRIDE_RECIPIENT_PREFIX = "wm_claim_override_recipient";

    /// @dev Registrar key prefix to determine the migrator contract.
    bytes32 internal constant _MIGRATOR_V1_PREFIX = "wm_migrator_v1";

    /// @inheritdoc IWrappedMToken
    address public immutable migrationAdmin;

    /// @inheritdoc IWrappedMToken
    address public immutable mToken;

    /// @inheritdoc IWrappedMToken
    address public immutable registrar;

    /// @inheritdoc IWrappedMToken
    address public immutable excessDestination;

    /// @inheritdoc IWrappedMToken
    uint112 public principalOfTotalEarningSupply;

    /// @inheritdoc IWrappedMToken
    uint240 public totalEarningSupply;

    /// @inheritdoc IWrappedMToken
    uint240 public totalNonEarningSupply;

    /// @dev Mapping of accounts to their respective `AccountInfo` structs.
    mapping(address account => Account balance) internal _accounts;

    /// @dev Array of indices at which earning was enabled or disabled.
    uint128[] internal _enableDisableEarningIndices;

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

    /**
     * @dev   Constructs the contract given an M Token address and migration admin.
     *        Note that a proxy will not need to initialize since there are no mutable storage values affected.
     * @param mToken_            The address of an M Token.
     * @param registrar_         The address of a Registrar.
     * @param excessDestination_ The address of an excess destination.
     * @param migrationAdmin_    The address of a migration admin.
     */
    constructor(
        address mToken_,
        address registrar_,
        address excessDestination_,
        address migrationAdmin_
    ) ERC20Extended("WrappedM by M^0", "wM", 6) {
        if ((mToken = mToken_) == address(0)) revert ZeroMToken();
        if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
        if ((excessDestination = excessDestination_) == address(0)) revert ZeroExcessDestination();
        if ((migrationAdmin = migrationAdmin_) == address(0)) revert ZeroMigrationAdmin();
    }

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

    /// @inheritdoc IWrappedMToken
    function wrap(address recipient_, uint256 amount_) external returns (uint240 wrapped_) {
        return _wrap(msg.sender, recipient_, UIntMath.safe240(amount_));
    }

    /// @inheritdoc IWrappedMToken
    function wrap(address recipient_) external returns (uint240 wrapped_) {
        return _wrap(msg.sender, recipient_, UIntMath.safe240(IMTokenLike(mToken).balanceOf(msg.sender)));
    }

    /// @inheritdoc IWrappedMToken
    function unwrap(address recipient_, uint256 amount_) external returns (uint240 unwrapped_) {
        return _unwrap(msg.sender, recipient_, UIntMath.safe240(amount_));
    }

    /// @inheritdoc IWrappedMToken
    function unwrap(address recipient_) external returns (uint240 unwrapped_) {
        return _unwrap(msg.sender, recipient_, uint240(balanceWithYieldOf(msg.sender)));
    }

    /// @inheritdoc IWrappedMToken
    function claimFor(address account_) external returns (uint240 yield_) {
        return _claim(account_, currentIndex());
    }

    /// @inheritdoc IWrappedMToken
    function claimExcess() external returns (uint240 excess_) {
        emit ExcessClaimed(excess_ = excess());

        IMTokenLike(mToken).transfer(excessDestination, excess_);
    }

    /// @inheritdoc IWrappedMToken
    function enableEarning() external {
        _revertIfNotApprovedEarner(address(this));

        if (isEarningEnabled()) revert EarningIsEnabled();

        // NOTE: This is a temporary measure to prevent re-enabling earning after it has been disabled.
        //       This line will be removed in the future.
        if (wasEarningEnabled()) revert EarningCannotBeReenabled();

        uint128 currentMIndex_ = _currentMIndex();

        _enableDisableEarningIndices.push(currentMIndex_);

        IMTokenLike(mToken).startEarning();

        emit EarningEnabled(currentMIndex_);
    }

    /// @inheritdoc IWrappedMToken
    function disableEarning() external {
        _revertIfApprovedEarner(address(this));

        if (!isEarningEnabled()) revert EarningIsDisabled();

        uint128 currentMIndex_ = _currentMIndex();

        _enableDisableEarningIndices.push(currentMIndex_);

        IMTokenLike(mToken).stopEarning();

        emit EarningDisabled(currentMIndex_);
    }

    /// @inheritdoc IWrappedMToken
    function startEarningFor(address account_) external {
        _revertIfNotApprovedEarner(account_);

        if (!isEarningEnabled()) revert EarningIsDisabled();

        Account storage accountInfo_ = _accounts[account_];

        if (accountInfo_.isEarning) return;

        // NOTE: Use `currentIndex()` if/when upgrading to support `startEarningFor` while earning is disabled.
        uint128 currentIndex_ = _currentMIndex();

        accountInfo_.isEarning = true;
        accountInfo_.lastIndex = currentIndex_;

        uint240 balance_ = accountInfo_.balance;

        _addTotalEarningSupply(balance_, currentIndex_);

        unchecked {
            totalNonEarningSupply -= balance_;
        }

        emit StartedEarning(account_);
    }

    /// @inheritdoc IWrappedMToken
    function stopEarningFor(address account_) external {
        _revertIfApprovedEarner(account_);

        uint128 currentIndex_ = currentIndex();

        _claim(account_, currentIndex_);

        Account storage accountInfo_ = _accounts[account_];

        if (!accountInfo_.isEarning) return;

        accountInfo_.isEarning = false;
        accountInfo_.lastIndex = 0;

        uint240 balance_ = accountInfo_.balance;

        _subtractTotalEarningSupply(balance_, currentIndex_);

        unchecked {
            totalNonEarningSupply += balance_;
        }

        emit StoppedEarning(account_);
    }

    /* ============ Temporary Admin Migration ============ */

    /// @inheritdoc IWrappedMToken
    function migrate(address migrator_) external {
        if (msg.sender != migrationAdmin) revert UnauthorizedMigration();

        _migrate(migrator_);
    }

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

    /// @inheritdoc IWrappedMToken
    function accruedYieldOf(address account_) public view returns (uint240 yield_) {
        Account storage accountInfo_ = _accounts[account_];

        if (!accountInfo_.isEarning) return 0;

        return _getAccruedYield(accountInfo_.balance, accountInfo_.lastIndex, currentIndex());
    }

    /// @inheritdoc IERC20
    function balanceOf(address account_) public view returns (uint256 balance_) {
        return _accounts[account_].balance;
    }

    /// @inheritdoc IWrappedMToken
    function balanceWithYieldOf(address account_) public view returns (uint256 balance_) {
        return balanceOf(account_) + accruedYieldOf(account_);
    }

    /// @inheritdoc IWrappedMToken
    function lastIndexOf(address account_) public view returns (uint128 lastIndex_) {
        return _accounts[account_].lastIndex;
    }

    /// @inheritdoc IWrappedMToken
    function claimOverrideRecipientFor(address account_) public view returns (address recipient_) {
        return
            address(
                uint160(
                    uint256(
                        IRegistrarLike(registrar).get(keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_PREFIX, account_)))
                    )
                )
            );
    }

    /// @inheritdoc IWrappedMToken
    function currentIndex() public view returns (uint128 index_) {
        return isEarningEnabled() ? _currentMIndex() : _lastDisableEarningIndex();
    }

    /// @inheritdoc IWrappedMToken
    function isEarning(address account_) external view returns (bool isEarning_) {
        return _accounts[account_].isEarning;
    }

    /// @inheritdoc IWrappedMToken
    function isEarningEnabled() public view returns (bool isEnabled_) {
        return _enableDisableEarningIndices.length % 2 == 1;
    }

    /// @inheritdoc IWrappedMToken
    function wasEarningEnabled() public view returns (bool wasEarning_) {
        return _enableDisableEarningIndices.length != 0;
    }

    /// @inheritdoc IWrappedMToken
    function excess() public view returns (uint240 excess_) {
        unchecked {
            uint128 currentIndex_ = currentIndex();
            uint240 balance_ = uint240(IMTokenLike(mToken).balanceOf(address(this)));
            uint240 earmarked_ = totalNonEarningSupply + _projectedEarningSupply(currentIndex_);

            return balance_ > earmarked_ ? _getSafeTransferableM(balance_ - earmarked_, currentIndex_) : 0;
        }
    }

    /// @inheritdoc IWrappedMToken
    function totalAccruedYield() external view returns (uint240 yield_) {
        uint240 projectedEarningSupply_ = _projectedEarningSupply(currentIndex());
        uint240 earningSupply_ = totalEarningSupply;

        unchecked {
            return projectedEarningSupply_ <= earningSupply_ ? 0 : projectedEarningSupply_ - earningSupply_;
        }
    }

    /// @inheritdoc IERC20
    function totalSupply() external view returns (uint256 totalSupply_) {
        return totalEarningSupply + totalNonEarningSupply;
    }

    /* ============ 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_, uint240 amount_) internal {
        _revertIfInsufficientAmount(amount_);
        _revertIfInvalidRecipient(recipient_);

        if (_accounts[recipient_].isEarning) {
            uint128 currentIndex_ = currentIndex();

            _claim(recipient_, currentIndex_);

            // NOTE: Additional principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`.
            _addEarningAmount(recipient_, amount_, currentIndex_);
        } else {
            _addNonEarningAmount(recipient_, 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_, uint240 amount_) internal {
        _revertIfInsufficientAmount(amount_);

        if (_accounts[account_].isEarning) {
            uint128 currentIndex_ = currentIndex();

            _claim(account_, currentIndex_);

            // NOTE: Subtracted principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`.
            _subtractEarningAmount(account_, amount_, currentIndex_);
        } else {
            _subtractNonEarningAmount(account_, amount_);
        }

        emit Transfer(account_, address(0), amount_);
    }

    /**
     * @dev   Increments the token balance of `account_` by `amount_`, assuming non-earning status.
     * @param account_ The address whose account balance will be incremented.
     * @param amount_  The present amount of tokens to increment by.
     */
    function _addNonEarningAmount(address account_, uint240 amount_) internal {
        // NOTE: Can be `unchecked` because the max amount of wrappable M is never greater than `type(uint240).max`.
        unchecked {
            _accounts[account_].balance += amount_;
            totalNonEarningSupply += amount_;
        }
    }

    /**
     * @dev   Decrements the token balance of `account_` by `amount_`, assuming non-earning status.
     * @param account_ The address whose account balance will be decremented.
     * @param amount_  The present amount of tokens to decrement by.
     */
    function _subtractNonEarningAmount(address account_, uint240 amount_) internal {
        unchecked {
            Account storage accountInfo_ = _accounts[account_];

            uint240 balance_ = accountInfo_.balance;

            if (balance_ < amount_) revert InsufficientBalance(account_, balance_, amount_);

            accountInfo_.balance = balance_ - amount_;
            totalNonEarningSupply -= amount_;
        }
    }

    /**
     * @dev   Increments the token balance of `account_` by `amount_`, assuming earning status and updated index.
     * @param account_      The address whose account balance will be incremented.
     * @param amount_       The present amount of tokens to increment by.
     * @param currentIndex_ The current index to use to compute the principal amount.
     */
    function _addEarningAmount(address account_, uint240 amount_, uint128 currentIndex_) internal {
        // NOTE: Can be `unchecked` because the max amount of wrappable M is never greater than `type(uint240).max`.
        unchecked {
            _accounts[account_].balance += amount_;
            _addTotalEarningSupply(amount_, currentIndex_);
        }
    }

    /**
     * @dev   Decrements the token balance of `account_` by `amount_`, assuming earning status and updated index.
     * @param account_      The address whose account balance will be decremented.
     * @param amount_       The present amount of tokens to decrement by.
     * @param currentIndex_ The current index to use to compute the principal amount.
     */
    function _subtractEarningAmount(address account_, uint240 amount_, uint128 currentIndex_) internal {
        unchecked {
            Account storage accountInfo_ = _accounts[account_];

            uint240 balance_ = accountInfo_.balance;

            if (balance_ < amount_) revert InsufficientBalance(account_, balance_, amount_);

            accountInfo_.balance = balance_ - amount_;
            _subtractTotalEarningSupply(amount_, currentIndex_);
        }
    }

    /**
     * @dev    Claims accrued yield for `account_` given a `currentIndex_`.
     * @param  account_      The address to claim accrued yield for.
     * @param  currentIndex_ The current index to accrue until.
     * @return yield_        The accrued yield that was claimed.
     */
    function _claim(address account_, uint128 currentIndex_) internal returns (uint240 yield_) {
        Account storage accountInfo_ = _accounts[account_];

        if (!accountInfo_.isEarning) return 0;

        uint128 index_ = accountInfo_.lastIndex;

        if (currentIndex_ == index_) return 0;

        uint240 startingBalance_ = accountInfo_.balance;

        yield_ = _getAccruedYield(startingBalance_, index_, currentIndex_);

        accountInfo_.lastIndex = currentIndex_;

        unchecked {
            if (yield_ == 0) return 0;

            accountInfo_.balance = startingBalance_ + yield_;

            // Update the total earning supply to account for the yield, but the principal has not changed.
            totalEarningSupply += yield_;
        }

        address claimOverrideRecipient_ = claimOverrideRecipientFor(account_);
        address claimRecipient_ = claimOverrideRecipient_ == address(0) ? account_ : claimOverrideRecipient_;

        // Emit the appropriate `Claimed` and `Transfer` events, depending on the claim override recipient
        emit Claimed(account_, claimRecipient_, yield_);
        emit Transfer(address(0), account_, yield_);

        if (claimRecipient_ != account_) {
            // NOTE: Watch out for a long chain of earning claim override recipients.
            _transfer(account_, claimRecipient_, yield_, currentIndex_);
        }
    }

    /**
     * @dev   Transfers `amount_` tokens from `sender_` to `recipient_` given some current index.
     * @param sender_       The sender's address.
     * @param recipient_    The recipient's address.
     * @param amount_       The amount to be transferred.
     * @param currentIndex_ The current index.
     */
    function _transfer(address sender_, address recipient_, uint240 amount_, uint128 currentIndex_) internal {
        _revertIfInvalidRecipient(recipient_);

        // Claims for both the sender and recipient are required before transferring since add an subtract functions
        // assume accounts' balances are up-to-date with the current index.
        _claim(sender_, currentIndex_);
        _claim(recipient_, currentIndex_);

        emit Transfer(sender_, recipient_, amount_);

        Account storage senderAccountInfo_ = _accounts[sender_];
        Account storage recipientAccountInfo_ = _accounts[recipient_];

        // If the sender and recipient are both earning or both non-earning, update their balances without affecting
        // the total earning and non-earning supply storage variables.
        if (senderAccountInfo_.isEarning == recipientAccountInfo_.isEarning) {
            uint240 senderBalance_ = senderAccountInfo_.balance;

            if (senderBalance_ < amount_) revert InsufficientBalance(sender_, senderBalance_, amount_);

            unchecked {
                senderAccountInfo_.balance = senderBalance_ - amount_;
                recipientAccountInfo_.balance += amount_;
            }

            return;
        }

        senderAccountInfo_.isEarning
            ? _subtractEarningAmount(sender_, amount_, currentIndex_)
            : _subtractNonEarningAmount(sender_, amount_);

        recipientAccountInfo_.isEarning
            ? _addEarningAmount(recipient_, amount_, currentIndex_)
            : _addNonEarningAmount(recipient_, amount_);
    }

    /**
     * @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 override {
        _transfer(sender_, recipient_, UIntMath.safe240(amount_), currentIndex());
    }

    /**
     * @dev   Increments total earning supply by `amount_` tokens.
     * @param amount_       The present amount of tokens to increment total earning supply by.
     * @param currentIndex_ The current index used to compute the principal amount.
     */
    function _addTotalEarningSupply(uint240 amount_, uint128 currentIndex_) internal {
        unchecked {
            // Increment the total earning supply and principal proportionally.
            totalEarningSupply += amount_;
            principalOfTotalEarningSupply += IndexingMath.getPrincipalAmountRoundedUp(amount_, currentIndex_);
        }
    }

    /**
     * @dev   Decrements total earning supply by `amount_` tokens.
     * @param amount_       The present amount of tokens to decrement total earning supply by.
     * @param currentIndex_ The current index used to compute the principal amount.
     */
    function _subtractTotalEarningSupply(uint240 amount_, uint128 currentIndex_) internal {
        if (amount_ >= totalEarningSupply) {
            totalEarningSupply = 0;
            principalOfTotalEarningSupply = 0;

            return;
        }

        unchecked {
            uint112 principal_ = IndexingMath.getPrincipalAmountRoundedDown(amount_, currentIndex_);

            principalOfTotalEarningSupply -= (
                principal_ > principalOfTotalEarningSupply ? principalOfTotalEarningSupply : principal_
            );

            totalEarningSupply -= amount_;
        }
    }

    /**
     * @dev    Wraps `amount` M from `account_` into wM for `recipient`.
     * @param  account_   The account from which M is deposited.
     * @param  recipient_ The account receiving the minted wM.
     * @param  amount_    The amount of M deposited.
     * @return wrapped_   The amount of wM minted.
     */
    function _wrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 wrapped_) {
        uint256 startingBalance_ = IMTokenLike(mToken).balanceOf(address(this));

        // NOTE: The behavior of `IMTokenLike.transferFrom` is known, so its return can be ignored.
        IMTokenLike(mToken).transferFrom(account_, address(this), amount_);

        // NOTE: When this WrappedMToken contract is earning, any amount of M sent to it is converted to a principal
        //       amount at the MToken contract, which when represented as a present amount, may be a rounding error
        //       amount less than `amount_`. In order to capture the real increase in M, the difference between the
        //       starting and ending M balance is minted as WrappedM.
        _mint(recipient_, wrapped_ = UIntMath.safe240(IMTokenLike(mToken).balanceOf(address(this)) - startingBalance_));
    }

    /**
     * @dev    Unwraps `amount` wM from `account_` into M for `recipient`.
     * @param  account_   The account from which WM is burned.
     * @param  recipient_ The account receiving the withdrawn M.
     * @param  amount_    The amount of wM burned.
     * @return unwrapped_ The amount of M withdrawn.
     */
    function _unwrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 unwrapped_) {
        _burn(account_, amount_);

        uint256 startingBalance_ = IMTokenLike(mToken).balanceOf(address(this));

        // NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored.
        IMTokenLike(mToken).transfer(recipient_, _getSafeTransferableM(amount_, currentIndex()));

        // NOTE: When this WrappedMToken contract is earning, any amount of M sent from it is converted to a principal
        //       amount at the MToken contract, which when represented as a present amount, may be a rounding error
        //       amount more than `amount_`. In order to capture the real decrease in M, the difference between the
        //       ending and starting M balance is returned.
        return UIntMath.safe240(startingBalance_ - IMTokenLike(mToken).balanceOf(address(this)));
    }

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

    /// @dev Returns the current index of the M Token.
    function _currentMIndex() internal view returns (uint128 index_) {
        return IMTokenLike(mToken).currentIndex();
    }

    /// @dev Returns the earning index from the last `disableEarning` call.
    function _lastDisableEarningIndex() internal view returns (uint128 index_) {
        return wasEarningEnabled() ? _unsafeAccess(_enableDisableEarningIndices, 1) : 0;
    }

    /**
     * @dev    Compute the yield given an account's balance, last index, and the current index.
     * @param  balance_      The token balance of an earning account.
     * @param  lastIndex_    The index of ast interaction for the account.
     * @param  currentIndex_ The current index.
     * @return yield_        The yield accrued since the last interaction.
     */
    function _getAccruedYield(
        uint240 balance_,
        uint128 lastIndex_,
        uint128 currentIndex_
    ) internal pure returns (uint240 yield_) {
        uint240 balanceWithYield_ = IndexingMath.getPresentAmountRoundedDown(
            IndexingMath.getPrincipalAmountRoundedDown(balance_, lastIndex_),
            currentIndex_
        );

        unchecked {
            return (balanceWithYield_ <= balance_) ? 0 : balanceWithYield_ - balance_;
        }
    }

    /**
     * @dev    Compute the adjusted amount of M that can safely be transferred out given the current index.
     * @param  amount_       Some amount to be transferred out of the wrapper.
     * @param  currentIndex_ The current index.
     * @return safeAmount_   The adjusted amount that can safely be transferred out.
     */
    function _getSafeTransferableM(uint240 amount_, uint128 currentIndex_) internal view returns (uint240 safeAmount_) {
        // If the wrapper is earning, adjust `amount_` to ensure it's M balance decrement is limited to `amount_`.
        return
            IMTokenLike(mToken).isEarning(address(this))
                ? IndexingMath.getPresentAmountRoundedDown(
                    IndexingMath.getPrincipalAmountRoundedDown(amount_, currentIndex_),
                    currentIndex_
                )
                : amount_;
    }

    /// @dev Returns the address of the contract to use as a migrator, if any.
    function _getMigrator() internal view override returns (address migrator_) {
        return
            address(
                uint160(
                    // NOTE: A subsequent implementation should use a unique migrator prefix.
                    uint256(IRegistrarLike(registrar).get(keccak256(abi.encode(_MIGRATOR_V1_PREFIX, address(this)))))
                )
            );
    }

    /**
     * @dev    Returns whether `account_` is a TTG-approved earner.
     * @param  account_    The account being queried.
     * @return isApproved_ True if the account_ is a TTG-approved earner, false otherwise.
     */
    function _isApprovedEarner(address account_) internal view returns (bool isApproved_) {
        return
            IRegistrarLike(registrar).get(_EARNERS_LIST_IGNORED) != bytes32(0) ||
            IRegistrarLike(registrar).listContains(_EARNERS_LIST, account_);
    }

    /**
     * @dev    Returns the projected total earning supply if all accrued yield was claimed at this moment.
     * @param  currentIndex_ The current index.
     * @return supply_       The projected total earning supply.
     */
    function _projectedEarningSupply(uint128 currentIndex_) internal view returns (uint240 supply_) {
        return IndexingMath.getPresentAmountRoundedDown(principalOfTotalEarningSupply, currentIndex_);
    }

    /**
     * @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 `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 `account_` is an approved earner.
     * @param account_ Address of an account.
     */
    function _revertIfApprovedEarner(address account_) internal view {
        if (_isApprovedEarner(account_)) revert IsApprovedEarner();
    }

    /**
     * @dev   Reverts if `account_` is not an approved earner.
     * @param account_ Address of an account.
     */
    function _revertIfNotApprovedEarner(address account_) internal view {
        if (!_isApprovedEarner(account_)) revert NotApprovedEarner();
    }

    /**
     * @dev   Reads the uint128 value at some index of an array of uint128 values whose storage pointer is given,
     *        assuming the index is valid, without wasting gas checking for out-of-bounds errors.
     * @param array_ The storage pointer of an array of uint128 values.
     * @param i_     The index of the array to read.
     */
    function _unsafeAccess(uint128[] storage array_, uint256 i_) internal view returns (uint128 value_) {
        assembly {
            mstore(0, array_.slot)

            value_ := sload(add(keccak256(0, 0x20), div(i_, 2)))

            // Since uint128 values take up either the top half or bottom half of a slot, shift the result accordingly.
            if eq(mod(i_, 2), 1) {
                value_ := shr(128, value_)
            }
        }
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

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

/**
 * @title  An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712 and with EIP-1271
 *         and EIP-5267 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
 * @author M^0 Labs
 */
abstract contract ERC20Extended is IERC20Extended, ERC3009 {
    /* ============ 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;

    /// @inheritdoc IERC20
    uint8 public immutable decimals;

    /// @dev The symbol of the token (stored as a bytes32 instead of a string in order to be immutable).
    bytes32 internal immutable _symbol;

    /// @inheritdoc IERC20
    mapping(address account => mapping(address spender => uint256 allowance)) public allowance;

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

    /**
     * @notice Constructs the ERC20Extended contract.
     * @param  name_     The name of the token.
     * @param  symbol_   The symbol of the token.
     * @param  decimals_ The number of decimals the token uses.
     */
    constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC3009(name_) {
        _symbol = Bytes32String.toBytes32(symbol_);
        decimals = decimals_;
    }

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

    /// @inheritdoc IERC20
    function approve(address spender_, uint256 amount_) external returns (bool success_) {
        _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 success_) {
        _transfer(msg.sender, recipient_, amount_);
        return true;
    }

    /// @inheritdoc IERC20
    function transferFrom(address sender_, address recipient_, uint256 amount_) external returns (bool success_) {
        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 name() external view returns (string memory) {
        return Bytes32String.toString(_name);
    }

    /// @inheritdoc IERC20
    function symbol() external view returns (string memory) {
        return Bytes32String.toString(_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(account_, spender_, amount_);
        emit Approval(account_, spender_, amount_);
    }

    /**
     * @dev Set the `amount_` of tokens `spender_` is allowed to spend from `account_`.
     * @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(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 digest_) {
        _revertIfExpired(deadline_);

        _approve(owner_, spender_, amount_);

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

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title  Subset of M Token interface required for source contracts.
 * @author M^0 Labs
 */
interface IMTokenLike {
    /* ============ Interactive Functions ============ */

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

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

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

    /// @notice Stops earning for caller.
    function stopEarning() 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 earning);

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

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

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

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

/**
 * @title  Subset of Registrar interface required for source contracts.
 * @author M^0 Labs
 */
interface IRegistrarLike {
    /* ============ View/Pure Functions ============ */

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

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

    /// @notice Returns the address of the Vault.
    function vault() external view returns (address vault);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

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

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

/**
 * @title  Wrapped M Token interface extending Extended ERC20.
 * @author M^0 Labs
 */
interface IWrappedMToken is IMigratable, IERC20Extended {
    /* ============ Events ============ */

    /**
     * @notice Emitted when some yield is claim for `account` to `recipient`.
     * @param  account   The account under which yield was generated.
     * @param  recipient The account that received the yield.
     * @param  yield     The amount of yield claimed.
     */
    event Claimed(address indexed account, address indexed recipient, uint240 yield);

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

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

    /**
     * @notice Emitted when the wrapper's excess M is claimed.
     * @param  excess The amount of excess M claimed.
     */
    event ExcessClaimed(uint240 excess);

    /**
     * @notice Emitted when `account` starts being an wM earner.
     * @param  account The account that started earning.
     */
    event StartedEarning(address indexed account);

    /**
     * @notice Emitted when `account` stops being an wM earner.
     * @param  account The account that stopped earning.
     */
    event StoppedEarning(address indexed account);

    /* ============ 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 trying to enable earning after it has been explicitly disabled.
    error EarningCannotBeReenabled();

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

    /**
     * @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, uint240 balance, uint240 amount);

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

    /// @notice Emitted when the non-governance migrate function is called by a account other than the migration admin.
    error UnauthorizedMigration();

    /// @notice Emitted in constructor if Excess Destination is 0x0.
    error ZeroExcessDestination();

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

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

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

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

    /**
     * @notice Wraps `amount` M from the caller into wM for `recipient`.
     * @param  recipient The account receiving the minted wM.
     * @param  amount    The amount of M deposited.
     * @return wrapped   The amount of wM minted.
     */
    function wrap(address recipient, uint256 amount) external returns (uint240 wrapped);

    /**
     * @notice Wraps all the M from the caller into wM for `recipient`.
     * @param  recipient The account receiving the minted wM.
     * @return wrapped   The amount of wM minted.
     */
    function wrap(address recipient) external returns (uint240 wrapped);

    /**
     * @notice Unwraps `amount` wM from the caller into M for `recipient`.
     * @param  recipient The account receiving the withdrawn M.
     * @param  amount    The amount of wM burned.
     * @return unwrapped The amount of M withdrawn.
     */
    function unwrap(address recipient, uint256 amount) external returns (uint240 unwrapped);

    /**
     * @notice Unwraps all the wM from the caller into M for `recipient`.
     * @param  recipient The account receiving the withdrawn M.
     * @return unwrapped The amount of M withdrawn.
     */
    function unwrap(address recipient) external returns (uint240 unwrapped);

    /**
     * @notice Claims any claimable yield for `account`.
     * @param  account The account under which yield was generated.
     * @return yield   The amount of yield claimed.
     */
    function claimFor(address account) external returns (uint240 yield);

    /**
     * @notice Claims any excess M of the wrapper.
     * @return excess The amount of excess claimed.
     */
    function claimExcess() external returns (uint240 excess);

    /// @notice Enables earning for the wrapper if allowed by TTG and if it has never been done.
    function enableEarning() external;

    /// @notice Disables earning for the wrapper if disallowed by TTG and if it has never been done.
    function disableEarning() external;

    /**
     * @notice Starts earning for `account` if allowed by TTG.
     * @param  account The account to start earning for.
     */
    function startEarningFor(address account) external;

    /**
     * @notice Stops earning for `account` if disallowed by TTG.
     * @param  account The account to stop earning for.
     */
    function stopEarningFor(address account) external;

    /* ============ Temporary Admin Migration ============ */

    /**
     * @notice Performs an arbitrarily defined migration.
     * @param  migrator The address of a migrator contract.
     */
    function migrate(address migrator) external;

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

    /**
     * @notice Returns the yield accrued for `account`, which is claimable.
     * @param  account The account being queried.
     * @return yield   The amount of yield that is claimable.
     */
    function accruedYieldOf(address account) external view returns (uint240 yield);

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

    /**
     * @notice Returns the last index of `account`.
     * @param  account   The address of some account.
     * @return lastIndex The last index of `account`, 0 if the account is not earning.
     */
    function lastIndexOf(address account) external view returns (uint128 lastIndex);

    /**
     * @notice Returns the recipient to override as the destination for an account's claim of yield.
     * @param  account   The account being queried.
     * @return recipient The address of the recipient, if any, to override as the destination of claimed yield.
     */
    function claimOverrideRecipientFor(address account) external view returns (address recipient);

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

    /// @notice The current excess M of the wrapper that is not earmarked for account balances or accrued yield.
    function excess() external view returns (uint240 excess);

    /**
     * @notice Returns whether `account` is a wM earner.
     * @param  account   The account being queried.
     * @return isEarning true if the account has started earning.
     */
    function isEarning(address account) external view returns (bool isEarning);

    /// @notice Whether earning is enabled for the entire wrapper.
    function isEarningEnabled() external view returns (bool isEnabled);

    /// @notice Whether earning has been enabled at least once or not.
    function wasEarningEnabled() external view returns (bool wasEnabled);

    /// @notice The account that can bypass TTG and call the `migrate(address migrator)` function.
    function migrationAdmin() external view returns (address migrationAdmin);

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

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

    /// @notice The portion of total supply that is not earning yield.
    function totalNonEarningSupply() external view returns (uint240 totalSupply);

    /// @notice The accrued yield of the portion of total supply that is earning yield.
    function totalAccruedYield() external view returns (uint240 yield);

    /// @notice The portion of total supply that is earning yield.
    function totalEarningSupply() external view returns (uint240 totalSupply);

    /// @notice The principal of totalEarningSupply to help compute totalAccruedYield(), and thus excess().
    function principalOfTotalEarningSupply() external view returns (uint112 principalOfTotalEarningSupply);

    /// @notice The address where excess is claimed to.
    function excessDestination() external view returns (address excessDestination);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.26;

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

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

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

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

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

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

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

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

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

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

        address oldImplementation_ = implementation();

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

        address newImplementation_ = implementation();

        emit Migrated(migrator_, oldImplementation_, newImplementation_);

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

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

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

// 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: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

/**
 * @title  A library to convert between string and bytes32 (assuming 32 characters or less).
 * @author M^0 Labs
 */
library Bytes32String {
    function toBytes32(string memory input) internal pure returns (bytes32) {
        return bytes32(abi.encodePacked(input));
    }

    function toString(bytes32 input) internal pure returns (string memory) {
        uint256 length;

        while (length < 32 && uint8(input[length]) != 0) {
            ++length;
        }

        bytes memory name = new bytes(length);

        for (uint256 index; index < length; ++index) {
            name[index] = input[index];
        }

        return string(name);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

/**
 * @title  ERC3009 implementation allowing the transfer of fungible assets via a signed authorization.
 * @author M^0 Labs
 * @dev    Inherits from ERC712 and StatefulERC712.
 */
abstract contract ERC3009 is IERC3009, StatefulERC712 {
    /* ============ 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;

    /// @inheritdoc IERC3009
    mapping(address authorizer => mapping(bytes32 nonce => bool isNonceUsed)) public authorizationState;

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

    /**
     * @notice Construct the ERC3009 contract.
     * @param  name_ The name of the contract.
     */
    constructor(string memory name_) StatefulERC712(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_);
    }

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

        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_);

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

pragma solidity 0.8.26;

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

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

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

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

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

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

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

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

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

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

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

File 15 of 21 : StatefulERC712.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

/**
 * @title  Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
 * @author M^0 Labs
 * @dev    An abstract implementation to satisfy stateful EIP-712 with nonces.
 */
abstract contract StatefulERC712 is IStatefulERC712, ERC712Extended {
    /// @inheritdoc IStatefulERC712
    mapping(address account => uint256 nonce) public nonces; // Nonces for all signatures.

    /**
     * @notice Construct the StatefulERC712 contract.
     * @param  name_ The name of the contract.
     */
    constructor(string memory name_) ERC712Extended(name_) {}
}

// 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 { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";

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

/**
 * @title  Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
 * @author M^0 Labs
 * @dev    An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
 */
abstract contract ERC712Extended is IERC712Extended {
    /* ============ 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;

    /// @dev Initial Chain ID set at deployment.
    uint256 internal immutable _INITIAL_CHAIN_ID;

    /// @dev Initial EIP-712 domain separator set at deployment.
    bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;

    /// @dev The name of the contract (stored as a bytes32 instead of a string in order to be immutable).
    bytes32 internal immutable _name;

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

    /**
     * @notice Constructs the EIP-712 domain separator.
     * @param  name_ The name of the contract.
     */
    constructor(string memory name_) {
        _name = Bytes32String.toBytes32(name_);

        _INITIAL_CHAIN_ID = block.chainid;
        _INITIAL_DOMAIN_SEPARATOR = _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
            Bytes32String.toString(_name),
            "1",
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /// @inheritdoc IERC712
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == _INITIAL_CHAIN_ID ? _INITIAL_DOMAIN_SEPARATOR : _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(Bytes32String.toString(_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": [
    "common/=lib/common/src/",
    "ds-test/=lib/native-token-transfers/evm/lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "native-token-transfers/=lib/native-token-transfers/",
    "openzeppelin-contracts/=lib/native-token-transfers/evm/lib/openzeppelin-contracts/",
    "protocol/=lib/protocol/",
    "solidity-bytes-utils/=lib/native-token-transfers/evm/lib/solidity-bytes-utils/contracts/",
    "ttg/=lib/ttg/",
    "wormhole-solidity-sdk/=lib/native-token-transfers/evm/lib/wormhole-solidity-sdk/src/",
    "wrapped-m-token/=lib/wrapped-m-token/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1500
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {
    "lib/native-token-transfers/evm/src/libraries/TransceiverStructs.sol": {
      "TransceiverStructs": "0x188B1E9E77B04E1699A78D929568Ed5BB15F962C"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"mToken_","type":"address"},{"internalType":"address","name":"registrar_","type":"address"},{"internalType":"address","name":"excessDestination_","type":"address"},{"internalType":"address","name":"migrationAdmin_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"DivisionByZero","type":"error"},{"inputs":[],"name":"EarningCannotBeReenabled","type":"error"},{"inputs":[],"name":"EarningIsDisabled","type":"error"},{"inputs":[],"name":"EarningIsEnabled","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":"uint240","name":"balance","type":"uint240"},{"internalType":"uint240","name":"amount","type":"uint240"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidMigrator","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":"InvalidUInt112","type":"error"},{"inputs":[],"name":"InvalidUInt240","type":"error"},{"inputs":[],"name":"IsApprovedEarner","type":"error"},{"inputs":[],"name":"MigrationFailed","type":"error"},{"inputs":[],"name":"NotApprovedEarner","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":"UnauthorizedMigration","type":"error"},{"inputs":[],"name":"ZeroExcessDestination","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroMigrationAdmin","type":"error"},{"inputs":[],"name":"ZeroMigrator","type":"error"},{"inputs":[],"name":"ZeroRegistrar","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":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint240","name":"yield","type":"uint240"}],"name":"Claimed","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":false,"internalType":"uint240","name":"excess","type":"uint240"}],"name":"ExcessClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"migrator","type":"address"},{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"StartedEarning","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"StoppedEarning","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":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","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":"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":[{"internalType":"address","name":"account_","type":"address"}],"name":"accruedYieldOf","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"allowance","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":"success_","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":"isNonceUsed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceWithYieldOf","outputs":[{"internalType":"uint256","name":"balance_","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":"claimExcess","outputs":[{"internalType":"uint240","name":"excess_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"claimFor","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"claimOverrideRecipientFor","outputs":[{"internalType":"address","name":"recipient_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"index_","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":[],"name":"excess","outputs":[{"internalType":"uint240","name":"excess_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"isEarning","outputs":[{"internalType":"bool","name":"isEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEarningEnabled","outputs":[{"internalType":"bool","name":"isEnabled_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"lastIndexOf","outputs":[{"internalType":"uint128","name":"lastIndex_","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_","type":"address"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationAdmin","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":"nonce","type":"uint256"}],"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":[],"name":"principalOfTotalEarningSupply","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"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":[],"name":"registrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"startEarningFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"stopEarningFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAccruedYield","outputs":[{"internalType":"uint240","name":"yield_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEarningSupply","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNonEarningSupply","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"totalSupply_","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":"success_","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":"success_","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":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"unwrap","outputs":[{"internalType":"uint240","name":"unwrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"}],"name":"unwrap","outputs":[{"internalType":"uint240","name":"unwrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wasEarningEnabled","outputs":[{"internalType":"bool","name":"wasEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"}],"name":"wrap","outputs":[{"internalType":"uint240","name":"wrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"wrap","outputs":[{"internalType":"uint240","name":"wrapped_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"}]

6101a0806040523461035757608081613cf68038038091610020828561035b565b8339810103126103575761003381610392565b9061004060208201610392565b90610059606061005260408401610392565b9201610392565b9060409283519461006a858761035b565b600f86526e0577261707065644d206279204d5e3608c1b60208701526100aa855196610096878961035b565b6002885261774d60f01b60208901526103c1565b958660c052466080525f5b6020811080610348575b156100e8575f1981146100d4576001016100b5565b634e487b7160e01b5f52601160045260245ffd5b876100f2826103a6565b906100ff8951928361035b565b82825261010b836103a6565b602083019390601f19013685375f5b81811061030f57505050906101a79291519020875160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8352898201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261019c60c08261035b565b51902060a0526103c1565b61010052600660e0526101408190526001600160a01b031615610300576101608190526001600160a01b0316156102f1576101808190526001600160a01b0316156102e2576101208190526001600160a01b0316156102d357516138ea908161040c823960805181611b80015260a05181611ba6015260c05181818161050601528181610bce0152611bcd015260e051816107dd01526101005181611095015261012051818181610a4801526115b101526101405181818161041e01528181610f0f01528181611216015281816113fe01528181611499015281816119ea01528181611f25015281816124a70152818161259101526126830152610160518181816107430152818161101a01528181611e5d0152612fd2015261018051818181610ec501526118690152f35b63ea0b51bb60e01b5f5260045ffd5b63c956307160e01b5f5260045ffd5b6379a6314960e01b5f5260045ffd5b63b01d5e2b60e01b5f5260045ffd5b60208110156103345783518110156103345780836001921a602082870101530161011a565b634e487b7160e01b5f52603260045260245ffd5b156103345787811a15156100bf565b5f80fd5b601f909101601f19168101906001600160401b0382119082101761037e57604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361035757565b6001600160401b03811161037e57601f01601f191660200190565b6040516103ed6020828180820195805191829101875e81015f838201520301601f19810183528261035b565b51905190602081106103fd575090565b5f199060200360031b1b169056fe60806040526004361015610011575f80fd5b5f3560e01c8063023276f0146103b457806306fdde03146103af578063095ea7b3146103aa578063174f6ce9146103a557806318160ddd146103a05780631ae2379c1461039b57806323b872dd1461039657806326987b6014610391578063281b229d1461038c5780632b20e397146103875780632c7861631461038257806330adf81f1461037d578063313ce567146103785780633644e5151461037357806339f476931461036e5780634c57a8fa14610369578063532992c51461036457806356d59ed21461035f5780635a049a701461035a5780635c60da1b146103555780635cf993841461035057806370a082311461034b57806374df25581461034657806374edaa201461034157806375f26e631461033c5780637c3ab944146103375780637ecebe00146103325780637f2eecc31461032d57806384af270f1461032857806384b0196e1461032357806388b7ab631461031e5780638a4a1017146103195780638a75f238146103145780638d78bf7e1461030f5780638fd3ab801461030a57806395d89b41146103055780639fd5a6cf14610300578063a08cb48b146102fb578063a0cc6a68146102f6578063a8afc01f146102f1578063a9059cbb146102ec578063ace150a5146102e7578063b7b72899146102e2578063bf376c7a146102dd578063c3b6f939146102d8578063c9144ddb146102d3578063c967891a146102ce578063ce5494bb146102c9578063cf092995146102c4578063d33627e5146102bf578063d505accf146102ba578063d9169487146102b5578063dd62ed3e146102b0578063ddeae033146102ab578063e3ee160e146102a6578063e94a0102146102a1578063e9ef148a1461029c5763ef55bec614610297575f80fd5b61188d565b61184a565b6117ff565b6117d4565b61174a565b6116f1565b6116b7565b611666565b611632565b611608565b61158e565b61144c565b611422565b6113df565b6113b6565b611369565b611339565b611303565b6111c9565b61118f565b611164565b6110b9565b61107d565b610f8b565b610e63565b610e3d565b610e1e565b610de9565b610bb6565b610b79565b610b3f565b610b08565b610ac4565b610a8d565b610a6c565b610a29565b6109e5565b6109c2565b61097d565b610935565b6108b7565b610879565b610853565b610823565b610801565b6107c4565b61078a565b610767565b610724565b6106fe565b6106d3565b61060d565b6105e2565b610592565b61056f565b61053e565b6104ee565b6103e9565b600435906001600160a01b03821682036103cf57565b5f80fd5b602435906001600160a01b03821682036103cf57565b346103cf5760203660031901126103cf576104026103b9565b6040516370a0823160e01b8152336004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156104bb5761046761046e92610488945f9161048c575b50611eb6565b9033611f05565b6040516001600160f01b0390911681529081906020820190565b0390f35b6104ae915060203d6020116104b4575b6104a68183610cfa565b8101906118b8565b5f610461565b503d61049c565b6118c7565b5f9103126103cf57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103cf575f3660031901126103cf5761048861052a7f00000000000000000000000000000000000000000000000000000000000000006120ba565b6040519182916020835260208301906104ca565b346103cf5760403660031901126103cf5761056461055a6103b9565b6024359033612124565b602060405160018152f35b346103cf5760203660031901126103cf5761059061058b6103b9565b6118d2565b005b346103cf575f3660031901126103cf576001600160f01b03600454166001600160f01b0360055416016001600160f01b0381116105dd576020906001600160f01b0360405191168152f35b6119b3565b346103cf575f3660031901126103cf5760206105fc6119c7565b6001600160f01b0360405191168152f35b346103cf5760603660031901126103cf576106266103b9565b61062e6103d3565b604435906001600160a01b0383165f52600260205261066060405f20336001600160a01b03165f5260205260405f2090565b549260018401610681575b610675935061253f565b60405160018152602090f35b82841061069d57610698836106759503338361250d565b61066b565b7f192b9e4e000000000000000000000000000000000000000000000000000000005f52336004526024849052604483905260645ffd5b346103cf575f3660031901126103cf5760206106ed611ada565b6001600160801b0360405191168152f35b346103cf575f3660031901126103cf5760206001600160f01b0360055416604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf5760203660031901126103cf5760206105fc6107856103b9565b611b2d565b346103cf575f3660031901126103cf5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b346103cf575f3660031901126103cf57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf575f3660031901126103cf57602061081b611b7d565b604051908152f35b346103cf5760403660031901126103cf5760206105fc6108416103b9565b61084c602435611eb6565b903361265b565b346103cf575f3660031901126103cf5760206001600160701b0360035416604051908152f35b346103cf5760803660031901126103cf576105906108956103b9565b602435906108b26064356044356108ac85856127c2565b8461281c565b612891565b346103cf575f3660031901126103cf576001600160f01b0364e8d4a510006108dd611ada565b6001600160801b036001600160701b036003541691160204166001600160f01b0360045416808211155f146109255750505f5b6040516001600160f01b039091168152602090f35b6001600160f01b03910316610910565b346103cf5760a03660031901126103cf5761094e6103b9565b6024356044359160ff831683036103cf576108b2610590936064356084359161097786866127c2565b856128ec565b346103cf575f3660031901126103cf5760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b0360405191168152f35b346103cf5760203660031901126103cf57602061081b6109e06103b9565b611c6f565b346103cf5760203660031901126103cf576001600160a01b03610a066103b9565b165f52600660205260206001600160f01b0360405f205460081c16604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf5760203660031901126103cf57610590610a886103b9565b611ca8565b346103cf5760203660031901126103cf5760206105fc610aab6103b9565b6001600160f01b03610abc33611c6f565b16903361265b565b346103cf5760203660031901126103cf576001600160a01b03610ae56103b9565b165f52600660205260206001600160801b03600160405f20015416604051908152f35b346103cf5760203660031901126103cf576001600160a01b03610b296103b9565b165f525f602052602060405f2054604051908152f35b346103cf575f3660031901126103cf5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b346103cf5760203660031901126103cf576001600160a01b03610b9a6103b9565b165f526006602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf57610c81610bf27f00000000000000000000000000000000000000000000000000000000000000006120ba565b6040516020610c018183610cfa565b5f8252601f1981013682840137610c8f604051610c1f604082610cfa565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e08701906104ca565b9085820360408701526104ca565b4660608501526001600160a01b03301660808501525f60a085015283810360c08501528180845192838152019301915f5b828110610ccf57505050500390f35b835185528695509381019392810192600101610cc0565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610d1c57604052565b610ce6565b67ffffffffffffffff8111610d1c57601f01601f191660200190565b81601f820112156103cf57803590610d5482610d21565b92610d626040519485610cfa565b828452602083830101116103cf57815f926020809301838601378301015290565b60e06003198201126103cf576004356001600160a01b03811681036103cf57916024356001600160a01b03811681036103cf579160443591606435916084359160a4359160c4359067ffffffffffffffff82116103cf57610de691600401610d3d565b90565b346103cf57610590610e19610dfd36610d83565b610e13828486888a8c989c9d979d9b999b6129df565b83612a56565b612b22565b346103cf575f3660031901126103cf5760206007541515604051908152f35b346103cf575f3660031901126103cf5760206001600160f01b0360045416604051908152f35b346103cf575f3660031901126103cf57610e7b6119c7565b7f49ec4048a361156a5338ff734a59a750f44cb5753bbe0886c6f39289bec2a01460206040516001600160f01b0384168152a160405163a9059cbb60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160f01b038216602482015290602082806044810103815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af19182156104bb5761048892610f5c575b506040516001600160f01b0390911681529081906020820190565b610f7d9060203d602011610f84575b610f758183610cfa565b810190611d7f565b505f610f41565b503d610f6b565b346103cf575f3660031901126103cf57604080517f776d5f6d69677261746f725f76310000000000000000000000000000000000006020828101918252309383019390935261100e9291610fec81606081015b03601f198101835282610cfa565b5190206040518093819263023aa9ab60e61b8352600483019190602083019252565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb57610590916001600160a01b03915f9161105e575b5016612ba4565b611077915060203d6020116104b4576104a68183610cfa565b5f611057565b346103cf575f3660031901126103cf5761048861052a7f00000000000000000000000000000000000000000000000000000000000000006120ba565b346103cf5760a03660031901126103cf576110d26103b9565b6110da6103d3565b6044359160843560643567ffffffffffffffff82116103cf576105909461110861110f933690600401610d3d565b9484612ce7565b90612a56565b6101009060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c4359060e43590565b346103cf57610590610e1961117836611115565b906108ac838587898b8d999d9e989e9c9a9c6129df565b346103cf575f3660031901126103cf5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b346103cf575f3660031901126103cf576111e23061216b565b6111f76111f3600180600754161490565b1590565b6112db5761120361255b565b61120c81611dab565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b156103cf575f80926004604051809581937f9f8495f90000000000000000000000000000000000000000000000000000000083525af19081156104bb577fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce926112bc926112c1575b506040516001600160801b0390911681529081906020820190565b0390a1005b806112cf5f6112d593610cfa565b806104c0565b5f6112a1565b7fb019ea35000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760403660031901126103cf5761056461131f6103b9565b61132a602435611eb6565b611332611ada565b91336130cf565b346103cf5761059061136461134d36611115565b906108ac838587898b8d999d9e989e9c9a9c612dae565b612e1b565b346103cf5760603660031901126103cf576113826103b9565b6024356044359167ffffffffffffffff83116103cf576108b26113ac610590943690600401610d3d565b610e1384846127c2565b346103cf5760403660031901126103cf5760206105fc6113d46103b9565b610467602435611eb6565b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf575f3660031901126103cf576020611442600180600754161490565b6040519015158152f35b346103cf575f3660031901126103cf57611465306128f8565b611473600180600754161490565b6115665760075461153e5761148661255b565b61148f81611dab565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b156103cf575f80926004604051809581937fa36e40fc0000000000000000000000000000000000000000000000000000000083525af19081156104bb577f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a18926112bc926112c157506040516001600160801b0390911681529081906020820190565b7f437b381f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7a427368000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760203660031901126103cf576115a76103b9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036115e05761059090612ba4565b7f51e2625e000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5761059061136461161c36610d83565b610e13828486888a8c989c9d979d9b999b612dae565b346103cf5760203660031901126103cf5760206116556116506103b9565b611e01565b6001600160a01b0360405191168152f35b346103cf5760e03660031901126103cf5761167f6103b9565b6116876103d3565b604435606435926084359060ff821682036103cf576116b16105909560a4359460c4359684612ce7565b906128ec565b346103cf575f3660031901126103cf5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b346103cf5760403660031901126103cf57602061174161170f6103b9565b6001600160a01b0361171f6103d3565b91165f526002835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b346103cf5760203660031901126103cf5760206105fc6117686103b9565b611770611ada565b906121a2565b6101209060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c43560ff811681036103cf579060e435906101043590565b346103cf576105906113646117e836611776565b916109778486888a8c989e988e9a9e9d9b9d612dae565b346103cf5760403660031901126103cf576118186103b9565b6001600160a01b0360243591165f52600160205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103cf57610590610e196118a136611776565b916109778486888a8c989e988e9a9e9d9b9d6129df565b908160209103126103cf575190565b6040513d5f823e3d90fd5b6118db8161216b565b6001600160a01b036118eb611ada565b916118f683826121a2565b501690815f52600660205260405f2060ff815416156119ae57611988916119516001600160f01b0361196c9360ff198154168155600181016fffffffffffffffffffffffffffffffff1981541690555460081c169182612393565b6001600160f01b0360055416906001600160f01b0391011690565b6001600160f01b03166001600160f01b03196005541617600555565b7f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a2565b505050565b634e487b7160e01b5f52601160045260245ffd5b6119cf611ada565b6040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156104bb576001600160f01b03915f91611abb575b501690611a8e611a426005546001600160f01b031690565b611a8183611a586001600160701b036003541690565b64e8d4a51000906001600160701b036001600160801b036001600160f01b039416911602041690565b016001600160f01b031690565b916001600160f01b038316811115611ab457610de6926001600160f01b0391031661246a565b5050505f90565b611ad4915060203d6020116104b4576104a68183610cfa565b5f611a2a565b611ae8600180600754161490565b15611af557610de661255b565b60075415611b295760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885460801c90565b5f90565b6001600160a01b03165f52600660205260405f20805460ff811615611b77576001600160801b036001610de6930154166001600160f01b03611b6d611ada565b9260081c16612603565b50505f90565b467f000000000000000000000000000000000000000000000000000000000000000003611bc8577f000000000000000000000000000000000000000000000000000000000000000090565b611bf17f00000000000000000000000000000000000000000000000000000000000000006120ba565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152611c6960c082610cfa565b51902090565b6001600160a01b0381165f5260066020526001600160f01b03611c9b8160405f205460081c1692611b2d565b1681018091116105dd5790565b611cb1816128f8565b611cbf600180600754161490565b156112db576001600160a01b0316805f52600660205260405f2060ff815416611d7b5761196c611d5591611d3a6001600160f01b03611cfc61255b565b835460ff19166001178455926001810180546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555460081c169182612930565b6001600160f01b0360055416906001600160f01b0391031690565b7f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a2565b5050565b908160209103126103cf575180151581036103cf5790565b634e487b7160e01b5f52603260045260245ffd5b6007549068010000000000000000821015610d1c576001820180600755821015611dfc5760075f5260205f208260011c01916001600160801b0380608085549360071b169316831b921b1916179055565b611d97565b604080517f776d5f636c61696d5f6f766572726964655f726563697069656e74000000000060208281019182526001600160a01b0390941682840152918152611e51929190610fec606082610cfa565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa80156104bb576001600160a01b03915f91611e9957501690565b611eb2915060203d6020116104b4576104a68183610cfa565b1690565b6001600160f01b038111611ed0576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919082039182116105dd57565b6040516370a0823160e01b81523060048201529193926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691602084602481865afa9384156104bb575f9461204f575b506040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201523060248201526001600160f01b03909116604482015260208180606481015b03815f865af180156104bb57612032575b506040516370a0823160e01b815230600482015290602090829060249082905afa9081156104bb576120119261200a92612005925f91612013575b50611ef8565b611eb6565b8093612efe565b565b61202c915060203d6020116104b4576104a68183610cfa565b5f611fff565b61204a9060203d602011610f8457610f758183610cfa565b611fc4565b60209194509161206e611fb393833d85116104b4576104a68183610cfa565b94915091611f5d565b9061208182610d21565b61208e6040519182610cfa565b828152809261209f601f1991610d21565b0190602036910137565b908151811015611dfc570160200190565b5f5b6020811080612115575b156120db575f1981146105dd576001016120bc565b906120e582612077565b915f5b8181106120f55750505090565b6020811015611dfc5780836001921a61210e82876120a9565b53016120e8565b15611dfc5781811a15156120c6565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259361215e86828961250d565b60405195865216941692a3565b61217490612f95565b61217a57565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916121c0816001600160a01b03165f52600660205260405f2090565b8054939060ff85161561238b5760018101946121e386546001600160801b031690565b906001600160801b0382166001600160801b03851614612381578361222081612219612245946001600160f01b039060081c1690565b9485612603565b97906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6001600160f01b03861615612378578154610100600160f81b03191690860160081b610100600160f81b03161790556122a961228d85611a816004546001600160f01b031690565b6001600160f01b03166001600160f01b03196004541617600455565b836122b383611e01565b6001600160a01b0381166123735750825b6001600160a01b0384166001600160a01b03821680827f4f3dc01c04a512d8b0b3b29dd31108ec4a93696f6506c2e8e609aa77953da7596040518061231989829190916001600160f01b036020820193169052565b0390a36040516001600160f01b038516815282905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a303612361575b50505050565b61236a936130cf565b5f80838161235b565b6122c4565b505f9450505050565b505f955050505050565b505f93505050565b906004546001600160f01b0381166001600160f01b038416101561244257508161241b6123f86123c961228d9461201196613736565b6001600160701b0360035416906001600160701b0382166001600160701b038216115f1461243457505f919050565b6001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b6004546001600160f01b0316036001600160f01b031690565b90036001600160701b031690565b6001600160f01b0319166004555050600380546dffffffffffffffffffffffffffff19169055565b906040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb575f916124ee575b50156124ea57611a5881610de693613736565b5090565b612507915060203d602011610f8457610f758183610cfa565b5f6124d7565b906001600160a01b0361253c92165f52600260205260405f20906001600160a01b03165f5260205260405f2090565b55565b9061254c61201193611eb6565b90612555611ada565b926130cf565b6040517f26987b600000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104bb575f916125c4575090565b90506020813d6020116125fb575b816125df60209383610cfa565b810103126103cf57516001600160801b03811681036103cf5790565b3d91506125d2565b9164e8d4a51000906001600160801b036001600160701b0361262d6001600160f01b039587613736565b169116020416906001600160f01b0381168211155f1461264d5750505f90565b6001600160f01b0391031690565b82612665916132a2565b6040516370a0823160e01b8152306004820152916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602084602481865afa9384156104bb575f94612796575b5061270c916126d56020926126cf611ada565b9061246a565b60405163a9059cbb60e01b81526001600160a01b0390921660048301526001600160f01b0316602482015291829081906044820190565b03815f865af180156104bb57612779575b506040516370a0823160e01b815230600482015290602090829060249082905afa80156104bb57610de692612005925f926127585750611ef8565b61277291925060203d6020116104b4576104a68183610cfa565b905f611fff565b6127919060203d602011610f8457610f758183610cfa565b61271d565b6020919450916126d56127b861270c94843d86116104b4576104a68183610cfa565b95925050916126bc565b610de691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298552166040830152606082015260608152612814608082610cfa565b519020613323565b9290916128559260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c01169061377b565b600682101561287d576120119282612876576128719250613760565b613371565b5050613371565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b03906128a48382613499565b16805f52600160205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b9391612855939161377b565b61290190612f95565b1561290857565b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b906001600160801b03906004546001600160f01b03196001600160f01b038581841601169116176004551680156129b757612011916001600160701b039161298d916001600160f01b031664e8d4a510000281015f190104613872565b816003541601166001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b939092610de695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b6006111561287d57565b91612a618183613843565b600682101561287d5781612b1b57612a7a915084613760565b925b612a8584612a4c565b831561235b575f92610fde612ac085946040519283916020830195630b135d3f60e11b875260248401526040604484015260648301906104ca565b51915afa612acc612b75565b81612b0d575b81612ae9575b50612ae65761201190613371565b50565b630b135d3f60e11b915080602080612b06935183010191016118b8565b145f612ad8565b905060208151101590612ad2565b5092612a7c565b9594939291906001600160a01b03811696873303612b4557612011969750612e1b565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d15612b9f573d90612b8682610d21565b91612b946040519384610cfa565b82523d5f602084013e565b606090565b6001600160a01b038116908115612cbf57803b15612c97575f8080807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54945af4612bed612b75565b5015612c6f576001600160a01b0391827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541692839216907fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a5f80a47fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2565b7fa27bfda2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f8d1e7cf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f0d626a32000000000000000000000000000000000000000000000000000000005f5260045ffd5b939293844211612d7e57936001600160a01b0385612d0a8585610de69899612124565b1692835f525f60205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c0815261281460e082610cfa565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b939092610de695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b9390929594919580421115612ecf575080421015612ea05750612011939481612e4761254c9386613499565b6001600160a01b038516805f52600160205260405f20825f52602052612e7760405f20600160ff19825416179055565b7f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a3611eb6565b7fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f926001600160f01b03851690612f37826134f2565b612f4081613526565b6001600160a01b038116958686526006845260ff6040872054168614612f875790612f7e91612f6d611ada565b91612f7883826121a2565b506135cf565b604051908152a3565b612f9091613563565b612f7e565b60405163023aa9ab60e61b81527f6561726e6572735f6c6973745f69676e6f72656400000000000000000000000060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602081602481865afa9081156104bb575f916130b0575b50159182159261301a57505090565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081527f6561726e6572730000000000000000000000000000000000000000000000000060048201526001600160a01b03929092166024830152909150602090829060449082905afa9081156104bb575f91613097575090565b610de6915060203d602011610f8457610f758183610cfa565b6130c9915060203d6020116104b4576104a68183610cfa565b5f61300b565b929190926130dc84613526565b6130e683826121a2565b506130f183856121a2565b506040516001600160f01b03831681526001600160a01b0385811691908316907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3613152816001600160a01b03165f52600660205260405f2090565b9061316e856001600160a01b03165f52600660205260405f2090565b91805461317b8160ff1690565b61318e613189865460ff1690565b151590565b901515146131e757509084846131a96131be95945460ff1690565b156131d7576131b792613696565b5460ff1690565b156131cc57612011926135cf565b905061201191613563565b90506131e29161361d565b6131b7565b9295509293509060081c6001600160f01b0316906001600160f01b0384166001600160f01b0383161061327357508154610100600160f81b0319169083900360081b610100600160f81b0316179055612011919061325290825460081c6001600160f01b0316611a81565b610100600160f81b0319610100600160f81b0383549260081b169116179055565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b0390811660245290911660445260645ffd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f936001600160f01b038416906132dc826134f2565b6001600160a01b038116948587526006845260ff604088205416871461331a5790612f7e91613309611ada565b9161331483826121a2565b50613696565b612f909161361d565b61332b611b7d565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611c69606282610cfa565b9061337b82612a4c565b8115613495575061338b81612a4c565b600181146134865761339c81612a4c565b6002811461345e576133ad81612a4c565b60038114613436576133be81612a4c565b6004811461340e57806133d2600592612a4c565b146133e657638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600160205260405f20825f5260205260ff60405f2054166134c4575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b80156134fb5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b031680156135385750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b03165f5260066020526135ad60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6001600160f01b03196001600160f01b03600554928184160116911617600555565b6001600160a01b03165f9081526006602052604090208054610100600160f81b03198116600891821c6001600160f01b0316840190911b610100600160f81b03161790556120119190612930565b6001600160a01b0381165f52600660205260405f20906001600160f01b03825460081c16906001600160f01b038416821061327357508154610100600160f81b0319169083900360081b610100600160f81b03161790556001600160f01b03196001600160f01b03600554928184160316911617600555565b9291906001600160a01b0384165f52600660205260405f20936001600160f01b03855460081c16906001600160f01b0383168210613709575061201193946001600160f01b0383613704930316610100600160f81b0319610100600160f81b0383549260081b169116179055565b612393565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b039081166024521660445260645ffd5b906001600160801b031680156129b75764e8d4a510006001600160f01b03610de693160204613872565b6001600160a01b03908116911603613776575f90565b600590565b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116138385760ff8216601b811415908161382c575b50613821576137e85f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156104bb575f516001600160a01b03811661380e57506001905f90565b5f906001600160a01b03169091565b9091565b505050506004905f90565b601c915014155f6137b5565b505050506003905f90565b9060418151036138695761381d91602082015190606060408401519301515f1a9061377b565b50506002905f90565b6001600160701b03811161388c576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220306a1ac6055685688633af84c0cf55b801c07e0727b7b7e4bd25a407d8b0017164736f6c634300081a0033000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c8063023276f0146103b457806306fdde03146103af578063095ea7b3146103aa578063174f6ce9146103a557806318160ddd146103a05780631ae2379c1461039b57806323b872dd1461039657806326987b6014610391578063281b229d1461038c5780632b20e397146103875780632c7861631461038257806330adf81f1461037d578063313ce567146103785780633644e5151461037357806339f476931461036e5780634c57a8fa14610369578063532992c51461036457806356d59ed21461035f5780635a049a701461035a5780635c60da1b146103555780635cf993841461035057806370a082311461034b57806374df25581461034657806374edaa201461034157806375f26e631461033c5780637c3ab944146103375780637ecebe00146103325780637f2eecc31461032d57806384af270f1461032857806384b0196e1461032357806388b7ab631461031e5780638a4a1017146103195780638a75f238146103145780638d78bf7e1461030f5780638fd3ab801461030a57806395d89b41146103055780639fd5a6cf14610300578063a08cb48b146102fb578063a0cc6a68146102f6578063a8afc01f146102f1578063a9059cbb146102ec578063ace150a5146102e7578063b7b72899146102e2578063bf376c7a146102dd578063c3b6f939146102d8578063c9144ddb146102d3578063c967891a146102ce578063ce5494bb146102c9578063cf092995146102c4578063d33627e5146102bf578063d505accf146102ba578063d9169487146102b5578063dd62ed3e146102b0578063ddeae033146102ab578063e3ee160e146102a6578063e94a0102146102a1578063e9ef148a1461029c5763ef55bec614610297575f80fd5b61188d565b61184a565b6117ff565b6117d4565b61174a565b6116f1565b6116b7565b611666565b611632565b611608565b61158e565b61144c565b611422565b6113df565b6113b6565b611369565b611339565b611303565b6111c9565b61118f565b611164565b6110b9565b61107d565b610f8b565b610e63565b610e3d565b610e1e565b610de9565b610bb6565b610b79565b610b3f565b610b08565b610ac4565b610a8d565b610a6c565b610a29565b6109e5565b6109c2565b61097d565b610935565b6108b7565b610879565b610853565b610823565b610801565b6107c4565b61078a565b610767565b610724565b6106fe565b6106d3565b61060d565b6105e2565b610592565b61056f565b61053e565b6104ee565b6103e9565b600435906001600160a01b03821682036103cf57565b5f80fd5b602435906001600160a01b03821682036103cf57565b346103cf5760203660031901126103cf576104026103b9565b6040516370a0823160e01b8152336004820152906020826024817f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03165afa9081156104bb5761046761046e92610488945f9161048c575b50611eb6565b9033611f05565b6040516001600160f01b0390911681529081906020820190565b0390f35b6104ae915060203d6020116104b4575b6104a68183610cfa565b8101906118b8565b5f610461565b503d61049c565b6118c7565b5f9103126103cf57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103cf575f3660031901126103cf5761048861052a7f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6040519182916020835260208301906104ca565b346103cf5760403660031901126103cf5761056461055a6103b9565b6024359033612124565b602060405160018152f35b346103cf5760203660031901126103cf5761059061058b6103b9565b6118d2565b005b346103cf575f3660031901126103cf576001600160f01b03600454166001600160f01b0360055416016001600160f01b0381116105dd576020906001600160f01b0360405191168152f35b6119b3565b346103cf575f3660031901126103cf5760206105fc6119c7565b6001600160f01b0360405191168152f35b346103cf5760603660031901126103cf576106266103b9565b61062e6103d3565b604435906001600160a01b0383165f52600260205261066060405f20336001600160a01b03165f5260205260405f2090565b549260018401610681575b610675935061253f565b60405160018152602090f35b82841061069d57610698836106759503338361250d565b61066b565b7f192b9e4e000000000000000000000000000000000000000000000000000000005f52336004526024849052604483905260645ffd5b346103cf575f3660031901126103cf5760206106ed611ada565b6001600160801b0360405191168152f35b346103cf575f3660031901126103cf5760206001600160f01b0360055416604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c168152f35b346103cf5760203660031901126103cf5760206105fc6107856103b9565b611b2d565b346103cf575f3660031901126103cf5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b346103cf575f3660031901126103cf57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b346103cf575f3660031901126103cf57602061081b611b7d565b604051908152f35b346103cf5760403660031901126103cf5760206105fc6108416103b9565b61084c602435611eb6565b903361265b565b346103cf575f3660031901126103cf5760206001600160701b0360035416604051908152f35b346103cf5760803660031901126103cf576105906108956103b9565b602435906108b26064356044356108ac85856127c2565b8461281c565b612891565b346103cf575f3660031901126103cf576001600160f01b0364e8d4a510006108dd611ada565b6001600160801b036001600160701b036003541691160204166001600160f01b0360045416808211155f146109255750505f5b6040516001600160f01b039091168152602090f35b6001600160f01b03910316610910565b346103cf5760a03660031901126103cf5761094e6103b9565b6024356044359160ff831683036103cf576108b2610590936064356084359161097786866127c2565b856128ec565b346103cf575f3660031901126103cf5760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b0360405191168152f35b346103cf5760203660031901126103cf57602061081b6109e06103b9565b611c6f565b346103cf5760203660031901126103cf576001600160a01b03610a066103b9565b165f52600660205260206001600160f01b0360405f205460081c16604051908152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1168152f35b346103cf5760203660031901126103cf57610590610a886103b9565b611ca8565b346103cf5760203660031901126103cf5760206105fc610aab6103b9565b6001600160f01b03610abc33611c6f565b16903361265b565b346103cf5760203660031901126103cf576001600160a01b03610ae56103b9565b165f52600660205260206001600160801b03600160405f20015416604051908152f35b346103cf5760203660031901126103cf576001600160a01b03610b296103b9565b165f525f602052602060405f2054604051908152f35b346103cf575f3660031901126103cf5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b346103cf5760203660031901126103cf576001600160a01b03610b9a6103b9565b165f526006602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf57610c81610bf27f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6040516020610c018183610cfa565b5f8252601f1981013682840137610c8f604051610c1f604082610cfa565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e08701906104ca565b9085820360408701526104ca565b4660608501526001600160a01b03301660808501525f60a085015283810360c08501528180845192838152019301915f5b828110610ccf57505050500390f35b835185528695509381019392810192600101610cc0565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610d1c57604052565b610ce6565b67ffffffffffffffff8111610d1c57601f01601f191660200190565b81601f820112156103cf57803590610d5482610d21565b92610d626040519485610cfa565b828452602083830101116103cf57815f926020809301838601378301015290565b60e06003198201126103cf576004356001600160a01b03811681036103cf57916024356001600160a01b03811681036103cf579160443591606435916084359160a4359160c4359067ffffffffffffffff82116103cf57610de691600401610d3d565b90565b346103cf57610590610e19610dfd36610d83565b610e13828486888a8c989c9d979d9b999b6129df565b83612a56565b612b22565b346103cf575f3660031901126103cf5760206007541515604051908152f35b346103cf575f3660031901126103cf5760206001600160f01b0360045416604051908152f35b346103cf575f3660031901126103cf57610e7b6119c7565b7f49ec4048a361156a5338ff734a59a750f44cb5753bbe0886c6f39289bec2a01460206040516001600160f01b0384168152a160405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde1660048201526001600160f01b038216602482015290602082806044810103815f6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165af19182156104bb5761048892610f5c575b506040516001600160f01b0390911681529081906020820190565b610f7d9060203d602011610f84575b610f758183610cfa565b810190611d7f565b505f610f41565b503d610f6b565b346103cf575f3660031901126103cf57604080517f776d5f6d69677261746f725f76310000000000000000000000000000000000006020828101918252309383019390935261100e9291610fec81606081015b03601f198101835282610cfa565b5190206040518093819263023aa9ab60e61b8352600483019190602083019252565b03816001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c165afa9081156104bb57610590916001600160a01b03915f9161105e575b5016612ba4565b611077915060203d6020116104b4576104a68183610cfa565b5f611057565b346103cf575f3660031901126103cf5761048861052a7f774d0000000000000000000000000000000000000000000000000000000000006120ba565b346103cf5760a03660031901126103cf576110d26103b9565b6110da6103d3565b6044359160843560643567ffffffffffffffff82116103cf576105909461110861110f933690600401610d3d565b9484612ce7565b90612a56565b6101009060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c4359060e43590565b346103cf57610590610e1961117836611115565b906108ac838587898b8d999d9e989e9c9a9c6129df565b346103cf575f3660031901126103cf5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b346103cf575f3660031901126103cf576111e23061216b565b6111f76111f3600180600754161490565b1590565b6112db5761120361255b565b61120c81611dab565b6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1690813b156103cf575f80926004604051809581937f9f8495f90000000000000000000000000000000000000000000000000000000083525af19081156104bb577fee580fdb4da10ea17aa673e6f5c8c2370b4166d6a94bc88900e5a96d0589e3ce926112bc926112c1575b506040516001600160801b0390911681529081906020820190565b0390a1005b806112cf5f6112d593610cfa565b806104c0565b5f6112a1565b7fb019ea35000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760403660031901126103cf5761056461131f6103b9565b61132a602435611eb6565b611332611ada565b91336130cf565b346103cf5761059061136461134d36611115565b906108ac838587898b8d999d9e989e9c9a9c612dae565b612e1b565b346103cf5760603660031901126103cf576113826103b9565b6024356044359167ffffffffffffffff83116103cf576108b26113ac610590943690600401610d3d565b610e1384846127c2565b346103cf5760403660031901126103cf5760206105fc6113d46103b9565b610467602435611eb6565b346103cf575f3660031901126103cf5760206040516001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b168152f35b346103cf575f3660031901126103cf576020611442600180600754161490565b6040519015158152f35b346103cf575f3660031901126103cf57611465306128f8565b611473600180600754161490565b6115665760075461153e5761148661255b565b61148f81611dab565b6001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1690813b156103cf575f80926004604051809581937fa36e40fc0000000000000000000000000000000000000000000000000000000083525af19081156104bb577f5098de6eb11dbd1127cf4dcd5e960e3944d48a7570b9b1939cff715cb35c5a18926112bc926112c157506040516001600160801b0390911681529081906020820190565b7f437b381f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7a427368000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5760203660031901126103cf576115a76103b9565b6001600160a01b037f000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd11633036115e05761059090612ba4565b7f51e2625e000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103cf5761059061136461161c36610d83565b610e13828486888a8c989c9d979d9b999b612dae565b346103cf5760203660031901126103cf5760206116556116506103b9565b611e01565b6001600160a01b0360405191168152f35b346103cf5760e03660031901126103cf5761167f6103b9565b6116876103d3565b604435606435926084359060ff821682036103cf576116b16105909560a4359460c4359684612ce7565b906128ec565b346103cf575f3660031901126103cf5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b346103cf5760403660031901126103cf57602061174161170f6103b9565b6001600160a01b0361171f6103d3565b91165f526002835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b346103cf5760203660031901126103cf5760206105fc6117686103b9565b611770611ada565b906121a2565b6101209060031901126103cf576004356001600160a01b03811681036103cf57906024356001600160a01b03811681036103cf579060443590606435906084359060a4359060c43560ff811681036103cf579060e435906101043590565b346103cf576105906113646117e836611776565b916109778486888a8c989e988e9a9e9d9b9d612dae565b346103cf5760403660031901126103cf576118186103b9565b6001600160a01b0360243591165f52600160205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103cf575f3660031901126103cf5760206040516001600160a01b037f0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde168152f35b346103cf57610590610e196118a136611776565b916109778486888a8c989e988e9a9e9d9b9d6129df565b908160209103126103cf575190565b6040513d5f823e3d90fd5b6118db8161216b565b6001600160a01b036118eb611ada565b916118f683826121a2565b501690815f52600660205260405f2060ff815416156119ae57611988916119516001600160f01b0361196c9360ff198154168155600181016fffffffffffffffffffffffffffffffff1981541690555460081c169182612393565b6001600160f01b0360055416906001600160f01b0391011690565b6001600160f01b03166001600160f01b03196005541617600555565b7f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a2565b505050565b634e487b7160e01b5f52601160045260245ffd5b6119cf611ada565b6040516370a0823160e01b81523060048201526020816024817f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b6001600160a01b03165afa80156104bb576001600160f01b03915f91611abb575b501690611a8e611a426005546001600160f01b031690565b611a8183611a586001600160701b036003541690565b64e8d4a51000906001600160701b036001600160801b036001600160f01b039416911602041690565b016001600160f01b031690565b916001600160f01b038316811115611ab457610de6926001600160f01b0391031661246a565b5050505f90565b611ad4915060203d6020116104b4576104a68183610cfa565b5f611a2a565b611ae8600180600754161490565b15611af557610de661255b565b60075415611b295760075f527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885460801c90565b5f90565b6001600160a01b03165f52600660205260405f20805460ff811615611b77576001600160801b036001610de6930154166001600160f01b03611b6d611ada565b9260081c16612603565b50505f90565b467f000000000000000000000000000000000000000000000000000000000000a4b103611bc8577f3027ba94c91fce0fc24681fb321a3428aa60b6207230bedee4912d3609c52ff990565b611bf17f577261707065644d206279204d5e3000000000000000000000000000000000006120ba565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152611c6960c082610cfa565b51902090565b6001600160a01b0381165f5260066020526001600160f01b03611c9b8160405f205460081c1692611b2d565b1681018091116105dd5790565b611cb1816128f8565b611cbf600180600754161490565b156112db576001600160a01b0316805f52600660205260405f2060ff815416611d7b5761196c611d5591611d3a6001600160f01b03611cfc61255b565b835460ff19166001178455926001810180546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555460081c169182612930565b6001600160f01b0360055416906001600160f01b0391031690565b7f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a2565b5050565b908160209103126103cf575180151581036103cf5790565b634e487b7160e01b5f52603260045260245ffd5b6007549068010000000000000000821015610d1c576001820180600755821015611dfc5760075f5260205f208260011c01916001600160801b0380608085549360071b169316831b921b1916179055565b611d97565b604080517f776d5f636c61696d5f6f766572726964655f726563697069656e74000000000060208281019182526001600160a01b0390941682840152918152611e51929190610fec606082610cfa565b03816001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c165afa80156104bb576001600160a01b03915f91611e9957501690565b611eb2915060203d6020116104b4576104a68183610cfa565b1690565b6001600160f01b038111611ed0576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919082039182116105dd57565b6040516370a0823160e01b81523060048201529193926001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b1691602084602481865afa9384156104bb575f9461204f575b506040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201523060248201526001600160f01b03909116604482015260208180606481015b03815f865af180156104bb57612032575b506040516370a0823160e01b815230600482015290602090829060249082905afa9081156104bb576120119261200a92612005925f91612013575b50611ef8565b611eb6565b8093612efe565b565b61202c915060203d6020116104b4576104a68183610cfa565b5f611fff565b61204a9060203d602011610f8457610f758183610cfa565b611fc4565b60209194509161206e611fb393833d85116104b4576104a68183610cfa565b94915091611f5d565b9061208182610d21565b61208e6040519182610cfa565b828152809261209f601f1991610d21565b0190602036910137565b908151811015611dfc570160200190565b5f5b6020811080612115575b156120db575f1981146105dd576001016120bc565b906120e582612077565b915f5b8181106120f55750505090565b6020811015611dfc5780836001921a61210e82876120a9565b53016120e8565b15611dfc5781811a15156120c6565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259361215e86828961250d565b60405195865216941692a3565b61217490612f95565b61217a57565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916121c0816001600160a01b03165f52600660205260405f2090565b8054939060ff85161561238b5760018101946121e386546001600160801b031690565b906001600160801b0382166001600160801b03851614612381578361222081612219612245946001600160f01b039060081c1690565b9485612603565b97906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6001600160f01b03861615612378578154610100600160f81b03191690860160081b610100600160f81b03161790556122a961228d85611a816004546001600160f01b031690565b6001600160f01b03166001600160f01b03196004541617600455565b836122b383611e01565b6001600160a01b0381166123735750825b6001600160a01b0384166001600160a01b03821680827f4f3dc01c04a512d8b0b3b29dd31108ec4a93696f6506c2e8e609aa77953da7596040518061231989829190916001600160f01b036020820193169052565b0390a36040516001600160f01b038516815282905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a303612361575b50505050565b61236a936130cf565b5f80838161235b565b6122c4565b505f9450505050565b505f955050505050565b505f93505050565b906004546001600160f01b0381166001600160f01b038416101561244257508161241b6123f86123c961228d9461201196613736565b6001600160701b0360035416906001600160701b0382166001600160701b038216115f1461243457505f919050565b6001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b6004546001600160f01b0316036001600160f01b031690565b90036001600160701b031690565b6001600160f01b0319166004555050600380546dffffffffffffffffffffffffffff19169055565b906040517f84af270f0000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165afa9081156104bb575f916124ee575b50156124ea57611a5881610de693613736565b5090565b612507915060203d602011610f8457610f758183610cfa565b5f6124d7565b906001600160a01b0361253c92165f52600260205260405f20906001600160a01b03165f5260205260405f2090565b55565b9061254c61201193611eb6565b90612555611ada565b926130cf565b6040517f26987b600000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b165afa9081156104bb575f916125c4575090565b90506020813d6020116125fb575b816125df60209383610cfa565b810103126103cf57516001600160801b03811681036103cf5790565b3d91506125d2565b9164e8d4a51000906001600160801b036001600160701b0361262d6001600160f01b039587613736565b169116020416906001600160f01b0381168211155f1461264d5750505f90565b6001600160f01b0391031690565b82612665916132a2565b6040516370a0823160e01b8152306004820152916001600160a01b037f000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b169190602084602481865afa9384156104bb575f94612796575b5061270c916126d56020926126cf611ada565b9061246a565b60405163a9059cbb60e01b81526001600160a01b0390921660048301526001600160f01b0316602482015291829081906044820190565b03815f865af180156104bb57612779575b506040516370a0823160e01b815230600482015290602090829060249082905afa80156104bb57610de692612005925f926127585750611ef8565b61277291925060203d6020116104b4576104a68183610cfa565b905f611fff565b6127919060203d602011610f8457610f758183610cfa565b61271d565b6020919450916126d56127b861270c94843d86116104b4576104a68183610cfa565b95925050916126bc565b610de691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298552166040830152606082015260608152612814608082610cfa565b519020613323565b9290916128559260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c01169061377b565b600682101561287d576120119282612876576128719250613760565b613371565b5050613371565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b03906128a48382613499565b16805f52600160205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b9391612855939161377b565b61290190612f95565b1561290857565b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b906001600160801b03906004546001600160f01b03196001600160f01b038581841601169116176004551680156129b757612011916001600160701b039161298d916001600160f01b031664e8d4a510000281015f190104613872565b816003541601166001600160701b03166dffffffffffffffffffffffffffff196003541617600355565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b939092610de695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b6006111561287d57565b91612a618183613843565b600682101561287d5781612b1b57612a7a915084613760565b925b612a8584612a4c565b831561235b575f92610fde612ac085946040519283916020830195630b135d3f60e11b875260248401526040604484015260648301906104ca565b51915afa612acc612b75565b81612b0d575b81612ae9575b50612ae65761201190613371565b50565b630b135d3f60e11b915080602080612b06935183010191016118b8565b145f612ad8565b905060208151101590612ad2565b5092612a7c565b9594939291906001600160a01b03811696873303612b4557612011969750612e1b565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b3d15612b9f573d90612b8682610d21565b91612b946040519384610cfa565b82523d5f602084013e565b606090565b6001600160a01b038116908115612cbf57803b15612c97575f8080807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54945af4612bed612b75565b5015612c6f576001600160a01b0391827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc541692839216907fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a5f80a47fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2565b7fa27bfda2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f8d1e7cf4000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f0d626a32000000000000000000000000000000000000000000000000000000005f5260045ffd5b939293844211612d7e57936001600160a01b0385612d0a8585610de69899612124565b1692835f525f60205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c0815261281460e082610cfa565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b939092610de695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e0815261281461010082610cfa565b9390929594919580421115612ecf575080421015612ea05750612011939481612e4761254c9386613499565b6001600160a01b038516805f52600160205260405f20825f52602052612e7760405f20600160ff19825416179055565b7f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a3611eb6565b7fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f926001600160f01b03851690612f37826134f2565b612f4081613526565b6001600160a01b038116958686526006845260ff6040872054168614612f875790612f7e91612f6d611ada565b91612f7883826121a2565b506135cf565b604051908152a3565b612f9091613563565b612f7e565b60405163023aa9ab60e61b81527f6561726e6572735f6c6973745f69676e6f72656400000000000000000000000060048201526001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169190602081602481865afa9081156104bb575f916130b0575b50159182159261301a57505090565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081527f6561726e6572730000000000000000000000000000000000000000000000000060048201526001600160a01b03929092166024830152909150602090829060449082905afa9081156104bb575f91613097575090565b610de6915060203d602011610f8457610f758183610cfa565b6130c9915060203d6020116104b4576104a68183610cfa565b5f61300b565b929190926130dc84613526565b6130e683826121a2565b506130f183856121a2565b506040516001600160f01b03831681526001600160a01b0385811691908316907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3613152816001600160a01b03165f52600660205260405f2090565b9061316e856001600160a01b03165f52600660205260405f2090565b91805461317b8160ff1690565b61318e613189865460ff1690565b151590565b901515146131e757509084846131a96131be95945460ff1690565b156131d7576131b792613696565b5460ff1690565b156131cc57612011926135cf565b905061201191613563565b90506131e29161361d565b6131b7565b9295509293509060081c6001600160f01b0316906001600160f01b0384166001600160f01b0383161061327357508154610100600160f81b0319169083900360081b610100600160f81b0316179055612011919061325290825460081c6001600160f01b0316611a81565b610100600160f81b0319610100600160f81b0383549260081b169116179055565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b0390811660245290911660445260645ffd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205f936001600160f01b038416906132dc826134f2565b6001600160a01b038116948587526006845260ff604088205416871461331a5790612f7e91613309611ada565b9161331483826121a2565b50613696565b612f909161361d565b61332b611b7d565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611c69606282610cfa565b9061337b82612a4c565b8115613495575061338b81612a4c565b600181146134865761339c81612a4c565b6002811461345e576133ad81612a4c565b60038114613436576133be81612a4c565b6004811461340e57806133d2600592612a4c565b146133e657638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600160205260405f20825f5260205260ff60405f2054166134c4575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b80156134fb5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b031680156135385750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160a01b03165f5260066020526135ad60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6001600160f01b03196001600160f01b03600554928184160116911617600555565b6001600160a01b03165f9081526006602052604090208054610100600160f81b03198116600891821c6001600160f01b0316840190911b610100600160f81b03161790556120119190612930565b6001600160a01b0381165f52600660205260405f20906001600160f01b03825460081c16906001600160f01b038416821061327357508154610100600160f81b0319169083900360081b610100600160f81b03161790556001600160f01b03196001600160f01b03600554928184160316911617600555565b9291906001600160a01b0384165f52600660205260405f20936001600160f01b03855460081c16906001600160f01b0383168210613709575061201193946001600160f01b0383613704930316610100600160f81b0319610100600160f81b0383549260081b169116179055565b612393565b6303629ea160e01b5f526001600160a01b03166004526001600160f01b039081166024521660445260645ffd5b906001600160801b031680156129b75764e8d4a510006001600160f01b03610de693160204613872565b6001600160a01b03908116911603613776575f90565b600590565b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116138385760ff8216601b811415908161382c575b50613821576137e85f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156104bb575f516001600160a01b03811661380e57506001905f90565b5f906001600160a01b03169091565b9091565b505050506004905f90565b601c915014155f6137b5565b505050506003905f90565b9060418151036138695761381d91602082015190606060408401519301515f1a9061377b565b50506002905f90565b6001600160701b03811161388c576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220306a1ac6055685688633af84c0cf55b801c07e0727b7b7e4bd25a407d8b0017164736f6c634300081a0033

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

000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1

-----Decoded View---------------
Arg [0] : mToken_ (address): 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b
Arg [1] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [2] : excessDestination_ (address): 0x3349e443068F76666789C4f76F00D9c4F38A4DdE
Arg [3] : migrationAdmin_ (address): 0xdcf79C332cB3Fe9d39A830a5f8de7cE6b1BD6fD1

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000866a2bf4e572cbcf37d5071a7a58503bfb36be1b
Arg [1] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [2] : 0000000000000000000000003349e443068f76666789c4f76f00d9c4f38a4dde
Arg [3] : 000000000000000000000000dcf79c332cb3fe9d39a830a5f8de7ce6b1bd6fd1


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

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.