ETH Price: $1,996.55 (+0.59%)

Contract

0xd7298f620B0F752Cf41BD818a16C756d9dCAA34f

Overview

ETH Balance

Linea Mainnet LogoLinea Mainnet LogoLinea Mainnet Logo0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Transaction Hash
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289533792026-02-18 3:23:4817 mins ago1771385028
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289530372026-02-18 2:54:1346 mins ago1771383253
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289527452026-02-18 2:32:371 hr ago1771381957
0xd7298f62...d9dCAA34f
0 ETH
289525962026-02-18 2:19:131 hr ago1771381153
0xd7298f62...d9dCAA34f
0 ETH
289525962026-02-18 2:19:131 hr ago1771381153
0xd7298f62...d9dCAA34f
0 ETH
289520072026-02-18 1:38:102 hrs ago1771378690
0xd7298f62...d9dCAA34f
0 ETH
289520072026-02-18 1:38:102 hrs ago1771378690
0xd7298f62...d9dCAA34f
0 ETH
289520072026-02-18 1:38:102 hrs ago1771378690
0xd7298f62...d9dCAA34f
0 ETH
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MToken

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
london EvmVersion
File 1 of 24 : MToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

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

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

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

import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol";
import { IMToken } from "./interfaces/IMToken.sol";

import { ContinuousIndexing } from "./abstract/ContinuousIndexing.sol";
import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol";

/**
 * @title  MToken
 * @author M^0 Labs
 * @notice ERC20 M Token living on other chains.
 */
contract MToken is IMToken, ContinuousIndexing, ERC20Extended, Migratable {
    /* ============ Structs ============ */

    /**
     * @notice MToken balance struct.
     * @param  isEarning  True if the account is earning, false otherwise.
     * @param  rawBalance Balance (for a non earning account) or balance principal (for an earning account).
     */
    struct MBalance {
        bool isEarning;
        uint240 rawBalance;
    }

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

    /// @inheritdoc IMToken
    address public immutable portal;

    /// @inheritdoc IMToken
    address public immutable registrar;

    /// @inheritdoc IMToken
    address public immutable migrationAdmin;

    /// @inheritdoc IMToken
    uint240 public totalNonEarningSupply;

    /// @inheritdoc IMToken
    uint112 public principalOfTotalEarningSupply;

    /// @notice The balance of M for non-earner or principal of earning M balance for earners.
    mapping(address account => MBalance balance) internal _balances;

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

    /// @dev Modifier to check if caller is the Portal.
    modifier onlyPortal() {
        _revertIfNotPortal();
        _;
    }

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

    /**
     * @notice Constructs the M Token contract.
     * @dev    Sets immutable storage.
     * @param  registrar_      The address of the Registrar contract.
     * @param  portal_         The address of the Portal contract.
     * @param  migrationAdmin_ The address of a migration admin.
     */
    constructor(address registrar_, address portal_, address migrationAdmin_) ContinuousIndexing() ERC20Extended("M by M^0", "M", 6) {
        _disableInitializers();
        
        if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
        if ((portal = portal_) == address(0)) revert ZeroPortal();
        if ((migrationAdmin = migrationAdmin_) == address(0)) revert ZeroMigrationAdmin();
    }

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

    /// @inheritdoc IMToken
    function initialize() external initializer {
        _initialize();
    }

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

    /// @inheritdoc IMToken
    function mint(address account_, uint256 amount_, uint128 index_) external onlyPortal {
        _updateIndex(index_);
        _mint(account_, amount_);
    }

    /// @inheritdoc IMToken
    function mint(address account_, uint256 amount_) external onlyPortal {
        _mint(account_, amount_);
    }

    /// @inheritdoc IMToken
    function burn(uint256 amount_) external onlyPortal {
        _burn(msg.sender, amount_);
    }

    /// @inheritdoc IMToken
    function updateIndex(uint128 index_) external onlyPortal {
        _updateIndex(index_);
    }

    /// @inheritdoc IMToken
    function startEarning() external {
        if (!_isApprovedEarner(msg.sender)) revert NotApprovedEarner();
        if (currentIndex() == ContinuousIndexingMath.EXP_SCALED_ONE) revert IndexNotInitialized();

        _startEarning(msg.sender);
    }

    /// @inheritdoc IMToken
    function stopEarning() external {
        _stopEarning(msg.sender);
    }

    /// @inheritdoc IMToken
    function stopEarning(address account_) external {
        if (_isApprovedEarner(account_)) revert IsApprovedEarner();

        _stopEarning(account_);
    }

    /**
     * @dev   Performs the contract migration by calling `migrator_`.
     * @param migrator_ The address of a migrator contract.
     */
    function migrate(address migrator_) external {
        if (msg.sender != migrationAdmin) revert UnauthorizedMigration();
        _migrate(migrator_);
    }

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

    /// @inheritdoc IMToken
    function totalEarningSupply() public view returns (uint240 totalEarningSupply_) {
        return _getPresentAmount(principalOfTotalEarningSupply);
    }

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

    /// @inheritdoc IMToken
    function principalBalanceOf(address account_) external view returns (uint240 balance_) {
        MBalance storage mBalance_ = _balances[account_];

        // Treat the raw balance as principal for earner.
        return mBalance_.isEarning ? uint112(mBalance_.rawBalance) : 0;
    }

    /// @inheritdoc IERC20
    function balanceOf(address account_) external view returns (uint256 balance_) {
        MBalance storage mBalance_ = _balances[account_];

        return
            mBalance_.isEarning
                ? _getPresentAmount(uint112(mBalance_.rawBalance)) // Treat the raw balance as principal for earner.
                : mBalance_.rawBalance;
    }

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

    /// @inheritdoc IContinuousIndexing
    function currentIndex() public view override(ContinuousIndexing, IContinuousIndexing) returns (uint128 index_) {
        return latestIndex;
    }

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

    /**
     * @dev   Adds principal to `_balances` of an earning account.
     * @param account_         The account to add principal to.
     * @param principalAmount_ The principal amount to add.
     */
    function _addEarningAmount(address account_, uint112 principalAmount_) internal {
        // NOTE: Safe to use unchecked here since overflow of the total supply is checked in `_mint`.
        unchecked {
            _balances[account_].rawBalance += principalAmount_;
            principalOfTotalEarningSupply += principalAmount_;
        }
    }

    /**
     * @dev   Adds amount to `_balances` of a non-earning account.
     * @param account_ The account to add amount to.
     * @param amount_  The amount to add.
     */
    function _addNonEarningAmount(address account_, uint240 amount_) internal {
        // NOTE: Safe to use unchecked here since overflow of the total supply is checked in `_mint`.
        unchecked {
            _balances[account_].rawBalance += amount_;
            totalNonEarningSupply += amount_;
        }
    }

    /**
     * @dev   Burns amount of earning or non-earning M from account.
     * @param account_ The account to burn from.
     * @param amount_  The present amount to burn.
     */
    function _burn(address account_, uint256 amount_) internal {
        _revertIfInsufficientAmount(amount_);

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

        if (_balances[account_].isEarning) {
            // NOTE: When burning a present amount, round the principal up in favor of the protocol.
            _subtractEarningAmount(account_, _getPrincipalAmountRoundedUp(UIntMath.safe240(amount_)));
        } else {
            _subtractNonEarningAmount(account_, UIntMath.safe240(amount_));
        }
    }

    /**
     * @dev   Mints amount of earning or non-earning M to account.
     * @param recipient_ The account to mint to.
     * @param amount_    The present amount to mint.
     */
    function _mint(address recipient_, uint256 amount_) internal {
        _revertIfInsufficientAmount(amount_);
        _revertIfInvalidRecipient(recipient_);

        emit Transfer(address(0), recipient_, amount_);

        uint240 safeAmount_ = UIntMath.safe240(amount_);

        unchecked {
            // As an edge case precaution, prevent a mint that, if all tokens (earning and non-earning) were converted
            // to a principal earning amount, would overflow the `uint112 principalOfTotalEarningSupply`.
            if (
                uint256(totalNonEarningSupply) + safeAmount_ > type(uint240).max ||
                // NOTE: Round the principal up for worst case.
                uint256(principalOfTotalEarningSupply) +
                    _getPrincipalAmountRoundedUp(totalNonEarningSupply + safeAmount_) >=
                type(uint112).max
            ) {
                revert OverflowsPrincipalOfTotalSupply();
            }
        }

        if (_balances[recipient_].isEarning) {
            // NOTE: When minting a present amount, round the principal down in favor of the protocol.
            _addEarningAmount(recipient_, _getPrincipalAmountRoundedDown(safeAmount_));
        } else {
            _addNonEarningAmount(recipient_, safeAmount_);
        }
    }

    /**
     * @dev   Starts earning for account.
     * @param account_ The account to start earning for.
     */
    function _startEarning(address account_) internal {
        MBalance storage mBalance_ = _balances[account_];

        if (mBalance_.isEarning) return;

        emit StartedEarning(account_);

        mBalance_.isEarning = true;

        // Treat the raw balance as present amount for non earner.
        uint240 amount_ = mBalance_.rawBalance;

        if (amount_ == 0) return;

        // NOTE: When converting a non-earning balance into an earning balance,
        // round the principal down in favor of the protocol.
        uint112 principalAmount_ = _getPrincipalAmountRoundedDown(amount_);

        _balances[account_].rawBalance = principalAmount_;

        unchecked {
            principalOfTotalEarningSupply += principalAmount_;
            totalNonEarningSupply -= amount_;
        }
    }

    /**
     * @dev   Stops earning for account.
     * @param account_ The account to stop earning for.
     */
    function _stopEarning(address account_) internal {
        MBalance storage mBalance_ = _balances[account_];

        if (!mBalance_.isEarning) return;

        emit StoppedEarning(account_);

        mBalance_.isEarning = false;

        // Treat the raw balance as principal for earner.
        uint112 principalAmount_ = uint112(_balances[account_].rawBalance);

        if (principalAmount_ == 0) return;

        uint240 amount_ = _getPresentAmount(principalAmount_);

        _balances[account_].rawBalance = amount_;

        unchecked {
            totalNonEarningSupply += amount_;
            principalOfTotalEarningSupply -= principalAmount_;
        }
    }

    /**
     * @dev   Subtracts principal from `_balances` of an earning account.
     * @param account_         The account to subtract principal from.
     * @param principalAmount_ The principal amount to subtract.
     */
    function _subtractEarningAmount(address account_, uint112 principalAmount_) internal {
        uint256 rawBalance_ = _balances[account_].rawBalance;

        if (rawBalance_ < principalAmount_) revert InsufficientBalance(account_, rawBalance_, principalAmount_);

        unchecked {
            // Overflow not possible given the above check.
            _balances[account_].rawBalance -= principalAmount_;
            principalOfTotalEarningSupply -= principalAmount_;
        }
    }

    /**
     * @dev   Subtracts amount from `_balances` of a non-earning account.
     * @param account_ The account to subtract amount from.
     * @param amount_  The amount to subtract.
     */
    function _subtractNonEarningAmount(address account_, uint240 amount_) internal {
        uint256 rawBalance_ = _balances[account_].rawBalance;

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

        unchecked {
            // Overflow not possible given the above check.
            _balances[account_].rawBalance -= amount_;
            totalNonEarningSupply -= amount_;
        }
    }

    /**
     * @dev   Transfer M between both earning and non-earning accounts.
     * @param sender_    The account to transfer from. It can be either earning or non-earning account.
     * @param recipient_ The account to transfer to. It can be either earning or non-earning account.
     * @param amount_    The present amount to transfer.
     */
    function _transfer(address sender_, address recipient_, uint256 amount_) internal override {
        _revertIfInvalidRecipient(recipient_);

        emit Transfer(sender_, recipient_, amount_);

        uint240 safeAmount_ = UIntMath.safe240(amount_);

        bool senderIsEarning_ = _balances[sender_].isEarning; // Only using the sender's earning status more than once.

        // If this is an in-kind transfer, then...
        if (senderIsEarning_ == _balances[recipient_].isEarning) {
            // NOTE: When subtracting a present amount from an earner, round the principal up in favor of the protocol.
            return
                _transferAmountInKind( // perform an in-kind transfer with...
                    sender_,
                    recipient_,
                    senderIsEarning_ ? _getPrincipalAmountRoundedUp(safeAmount_) : safeAmount_ // the appropriate amount
                );
        }

        // If this is not an in-kind transfer, then...
        if (senderIsEarning_) {
            // either the sender is earning and the recipient is not, or...
            // NOTE: When subtracting a present amount from an earner, round the principal up in favor of the protocol.
            _subtractEarningAmount(sender_, _getPrincipalAmountRoundedUp(safeAmount_));
            _addNonEarningAmount(recipient_, safeAmount_);
        } else {
            // the sender is not earning and the recipient is.
            // NOTE: When adding a present amount to an earner, round the principal down in favor of the protocol.
            _subtractNonEarningAmount(sender_, safeAmount_);
            _addEarningAmount(recipient_, _getPrincipalAmountRoundedDown(safeAmount_));
        }
    }

    /**
     * @dev   Transfer M between same earning status accounts.
     * @param sender_    The account to transfer from.
     * @param recipient_ The account to transfer to.
     * @param amount_    The amount (present or principal) to transfer.
     */
    function _transferAmountInKind(address sender_, address recipient_, uint240 amount_) internal {
        uint256 rawBalance_ = _balances[sender_].rawBalance;

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

        // NOTE: When transferring an amount in kind, the `rawBalance` can't overflow
        //       since the total supply would have overflowed first when minting.
        unchecked {
            _balances[sender_].rawBalance -= amount_;
            _balances[recipient_].rawBalance += amount_;
        }
    }

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

    /**
     * @dev    Returns the present amount (rounded down) given the principal amount, using the current index.
     *         All present amounts are rounded down in favor of the protocol.
     * @param  principalAmount_ The principal amount.
     * @return The present amount.
     */
    function _getPresentAmount(uint112 principalAmount_) internal view returns (uint240) {
        return _getPresentAmount(principalAmount_, currentIndex());
    }

    /**
     * @dev    Returns the present amount (rounded down) given the principal amount and an index.
     *         All present amounts are rounded down in favor of the protocol, since they are assets.
     * @param  principalAmount_ The principal amount.
     * @param  index_           An index
     * @return The present amount.
     */
    function _getPresentAmount(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) {
        return _getPresentAmountRoundedDown(principalAmount_, index_);
    }

    /**
     * @dev    Checks if earner was approved by the Registrar.
     * @param  account_ The account to check.
     * @return True if approved, false otherwise.
     */
    function _isApprovedEarner(address account_) internal view returns (bool) {
        return RegistrarReader.isEarnersListIgnored(registrar) || RegistrarReader.isApprovedEarner(registrar, account_);
    }

    /**
     * @dev   Reverts if the amount of a `mint` or `burn` is equal to 0.
     * @param amount_ Amount to check.
     */
    function _revertIfInsufficientAmount(uint256 amount_) internal pure {
        if (amount_ == 0) revert InsufficientAmount(amount_);
    }

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

    /// @dev Reverts if the caller is not the portal.
    function _revertIfNotPortal() internal view {
        if (msg.sender != portal) revert NotPortal();
    }

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

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

pragma solidity >=0.8.20 <0.9.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

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

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

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

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

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

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

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

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

        address oldImplementation_ = implementation();

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

        address newImplementation_ = implementation();

        emit Migrated(migrator_, oldImplementation_, newImplementation_);

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

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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

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

/**
 * @title  Library to read Registrar contract parameters.
 * @author M^0 Labs
 */
library RegistrarReader {
    /* ============ Variables ============ */

    /// @notice The parameter name in the Registrar that defines the earners list.
    bytes32 internal constant EARNERS_LIST = "earners";

    /// @notice The parameter name in the Registrar that defines whether to ignore the earners list.
    bytes32 internal constant EARNERS_LIST_IGNORED = "earners_list_ignored";

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

    /// @notice Checks if the given earner is approved.
    function isApprovedEarner(address registrar_, address earner_) internal view returns (bool) {
        return _contains(registrar_, EARNERS_LIST, earner_);
    }

    /// @notice Checks if the `earners_list_ignored` exists.
    function isEarnersListIgnored(address registrar_) internal view returns (bool) {
        return _get(registrar_, EARNERS_LIST_IGNORED) != bytes32(0);
    }

    /// @notice Gets the Portal contract address.
    function getPortal(address registrar_) internal view returns (address) {
        return IRegistrar(registrar_).portal();
    }

    /// @notice Checks if the given list contains the given account.
    function _contains(address registrar_, bytes32 listName_, address account_) private view returns (bool) {
        return IRegistrar(registrar_).listContains(listName_, account_);
    }

    /// @notice Gets the value of the given key.
    function _get(address registrar_, bytes32 key_) private view returns (bytes32) {
        return IRegistrar(registrar_).get(key_);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title  Continuous Indexing Interface.
 * @author M^0 Labs
 */
interface IContinuousIndexing {
    /* ============ Events ============ */

    /**
     * @notice Emitted when the index is updated.
     * @param  index The new index.
     */
    event IndexUpdated(uint128 indexed index);

    /* ============ Custom Error ============ */

    /**
     * @notice Emitted during index update when the new index is less than the current one.
     * @param  index The new index.
     * @param  currentIndex The current index.
     */
    error DecreasingIndex(uint128 index, uint128 currentIndex);

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

    /// @notice The current index that was last written to storage when `updatedIndex` was called.
    function currentIndex() external view returns (uint128);

    /// @notice The latest updated index.
    function latestIndex() external view returns (uint128);

    /// @notice The latest timestamp when the index was updated.
    function latestUpdateTimestamp() external view returns (uint40);
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

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

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

/**
 * @title  M Token Interface.
 * @author M^0 Labs
 */
interface IMToken is IContinuousIndexing, IERC20Extended {
    /* ============ Events ============ */

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

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

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

    /// @notice Emitted when the index from the Hub chain has not yet been propagated to the Spoke chain.
    error IndexNotInitialized();

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

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

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

    /// @notice Emitted when the caller is not the Portal.
    error NotPortal();

    /// @notice Emitted when principal of total supply (earning and non-earning) will overflow a `type(uint112).max`.
    error OverflowsPrincipalOfTotalSupply();

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

    /// @notice Emitted in constructor if the Portal address in the Registrar is 0x0.
    error ZeroPortal();

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

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

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

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

    /**
     * @notice Mints tokens.
     * @dev    MUST only be callable by the Spoke Portal.
     * @param  account The address of account to mint to.
     * @param  amount  The amount of M Token to mint.
     */
    function mint(address account, uint256 amount) external;

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

    /**
     * @notice Updates the latest index and latest accrual time in storage.
     * @dev    MUST only be callable by the Spoke Portal.
     * @param  index The new index to compute present amounts from principal amounts.
     */
    function updateIndex(uint128 index) external;

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

    /// @notice Stops earning for caller.
    function stopEarning() external;

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

    /// @notice Initializes the Proxy's storage.
    function initialize() external;

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

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

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

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

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

    /**
     * @notice The principal of an earner M token balance.
     * @param  account The account to get the principal balance of.
     * @return The principal balance of the account.
     */
    function principalBalanceOf(address account) external view returns (uint240);

    /// @notice The principal of the total earning supply of M Token.
    function principalOfTotalEarningSupply() external view returns (uint112);

    /// @notice The total earning supply of M Token.
    function totalEarningSupply() external view returns (uint240);

    /// @notice The total non-earning supply of M Token.
    function totalNonEarningSupply() external view returns (uint240);

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

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

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

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

/**
 * @title Abstract Continuous Indexing Contract to handle index updates in inheriting contracts.
 * @author M^0 Labs
 */
abstract contract ContinuousIndexing is IContinuousIndexing, Initializable {
    /* ============ Variables ============ */

    /// @inheritdoc IContinuousIndexing
    uint128 public latestIndex;

    /// @inheritdoc IContinuousIndexing
    uint40 public latestUpdateTimestamp;

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

    /// @notice Initializes Proxy's storage.
    function _initialize() internal onlyInitializing {
        latestIndex = ContinuousIndexingMath.EXP_SCALED_ONE;
        latestUpdateTimestamp = uint40(block.timestamp);
    }

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

    /// @inheritdoc IContinuousIndexing
    function currentIndex() public view virtual returns (uint128);

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

    /**
     * @notice Updates the latest index and latest accrual time.
     * @param  index_ The new index to compute present amounts from principal amounts.
     */
    function _updateIndex(uint128 index_) internal virtual {
        if (index_ < latestIndex) revert DecreasingIndex(index_, latestIndex);
        latestIndex = index_;
        latestUpdateTimestamp = uint40(block.timestamp);

        emit IndexUpdated(index_);
    }

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

    /**
     * @dev    Returns the principal amount (rounded down) given the present amount, using the current index.
     * @param  presentAmount_ The present amount.
     * @return The principal amount rounded down.
     */
    function _getPrincipalAmountRoundedDown(uint240 presentAmount_) internal view returns (uint112) {
        return _getPrincipalAmountRoundedDown(presentAmount_, currentIndex());
    }

    /**
     * @dev    Returns the principal amount (rounded up) given the present amount and an index.
     * @param  presentAmount_ The present amount.
     * @return The principal amount rounded up.
     */
    function _getPrincipalAmountRoundedUp(uint240 presentAmount_) internal view returns (uint112) {
        return _getPrincipalAmountRoundedUp(presentAmount_, currentIndex());
    }

    /**
     * @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 ContinuousIndexingMath.multiplyDown(principalAmount_, index_);
    }

    /**
     * @dev    Returns the present amount (rounded up) given the principal amount and an index.
     * @param  principalAmount_ The principal amount.
     * @param  index_           An index.
     * @return The present amount rounded up.
     */
    function _getPresentAmountRoundedUp(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) {
        return ContinuousIndexingMath.multiplyUp(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 ContinuousIndexingMath.divideDown(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 ContinuousIndexingMath.divideUp(presentAmount_, index_);
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

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

/**
 * @title  Arithmetic library with operations for calculating continuous indexing.
 * @author M^0 Labs
 */
library ContinuousIndexingMath {
    /* ============ Variables ============ */

    /// @notice The scaling of rates in 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) / index`, rounded down.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function divideDown(uint240 x, uint128 index) internal pure returns (uint112 z) {
        if (index == 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
            //       `multiplyDown` or `multiplyUp`, which would already satisfy
            //       `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
            return UIntMath.safe112((uint256(x) * EXP_SCALED_ONE) / index);
        }
    }

    /**
     * @notice Helper function to calculate `(x * EXP_SCALED_ONE) / index`, rounded up.
     * @dev    Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
     */
    function divideUp(uint240 x, uint128 index) internal pure returns (uint112 z) {
        if (index == 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
            //       `multiplyDown` or `multiplyUp`, which would already satisfy
            //       `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
            return UIntMath.safe112(((uint256(x) * EXP_SCALED_ONE) + index - 1) / index);
        }
    }

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

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

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

pragma solidity >=0.8.20 <0.9.0;

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

/**
 * @title  Registrar interface.
 * @author M^0 Labs
 */
interface IRegistrar {
    /**
     * @notice Key value pair getter.
     * @param  key The key to get the value of.
     * @return value The value of the key.
     */
    function get(bytes32 key) external view returns (bytes32 value);

    /**
     * @notice Checks if the list contains the account.
     * @param  list     The list to check.
     * @param  account  The account to check.
     * @return contains True if the list contains the account, false otherwise.
     */
    function listContains(bytes32 list, address account) external view returns (bool contains);

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

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

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.20 <0.9.0;

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 18 of 24 : 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": [
    "@ensdomains/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/=lib/uniswap-v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "@uniswap/v4-core/=lib/uniswap-v4-periphery/lib/v4-core/",
    "common/=lib/common/src/",
    "ds-test/=lib/uniswap-v4-periphery/lib/v4-core/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/uniswap-v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin/",
    "permit2/=lib/uniswap-v4-periphery/lib/permit2/",
    "protocol/=lib/protocol/",
    "solmate/=lib/uniswap-v4-periphery/lib/v4-core/lib/solmate/",
    "ttg/=lib/ttg/",
    "uniswap-v4-periphery/=lib/uniswap-v4-periphery/",
    "v4-core/=lib/uniswap-v4-periphery/lib/v4-core/src/",
    "wrapped-m-token/=lib/wrapped-m-token/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"registrar_","type":"address"},{"internalType":"address","name":"portal_","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":[{"internalType":"uint128","name":"index","type":"uint128"},{"internalType":"uint128","name":"currentIndex","type":"uint128"}],"name":"DecreasingIndex","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[],"name":"IndexNotInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"rawBalance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"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":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotPortal","type":"error"},{"inputs":[],"name":"OverflowsPrincipalOfTotalSupply","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":"ZeroMigrationAdmin","type":"error"},{"inputs":[],"name":"ZeroMigrator","type":"error"},{"inputs":[],"name":"ZeroPortal","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":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint128","name":"index","type":"uint128"}],"name":"IndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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"},{"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":"uint256","name":"amount_","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","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":"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":"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":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"isEarning","outputs":[{"internalType":"bool","name":"isEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestUpdateTimestamp","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"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":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint128","name":"index_","type":"uint128"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","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":"portal","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"principalBalanceOf","outputs":[{"internalType":"uint240","name":"balance_","type":"uint240"}],"stateMutability":"view","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":[],"name":"startEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"stopEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEarningSupply","outputs":[{"internalType":"uint240","name":"totalEarningSupply_","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":"uint128","name":"index_","type":"uint128"}],"name":"updateIndex","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61018060405234801561001157600080fd5b50604051613f4e380380613f4e833981016040819052610030916103ce565b6040518060400160405280600881526020016704d206279204d5e360c41b815250604051806040016040528060018152602001604d60f81b815250600682808061007f8161014360201b60201c565b60c0524660805261008e610174565b60a0525061009f9150839050610143565b6101005260ff1660e052506100b4905061020f565b6001600160a01b0383166101408190526100e1576040516379a6314960e01b815260040160405180910390fd5b6001600160a01b03821661012081905261010e57604051631b973f8d60e11b815260040160405180910390fd5b6001600160a01b03811661016081905261013b5760405163ea0b51bb60e01b815260040160405180910390fd5b5050506104ba565b6000816040516020016101569190610411565b60405160208183030381529060405261016e90610440565b92915050565b60c0516000907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f906101a5906102ad565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000610219610389565b805490915068010000000000000000900460ff161561024b5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102aa5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b606060005b6020811080156102d357508281602081106102cf576102cf610467565b1a15155b156102e8576102e18161047d565b90506102b2565b6000816001600160401b03811115610302576103026104a4565b6040519080825280601f01601f19166020018201604052801561032c576020820181803683370190505b50905060005b828110156103815784816020811061034c5761034c610467565b1a60f81b82828151811061036257610362610467565b60200101906001600160f81b031916908160001a905350600101610332565b509392505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061016e565b80516001600160a01b03811681146103c957600080fd5b919050565b6000806000606084860312156103e357600080fd5b6103ec846103b2565b92506103fa602085016103b2565b9150610408604085016103b2565b90509250925092565b6000825160005b818110156104325760208186018101518583015201610418565b506000920191825250919050565b80516020808301519190811015610461576000198160200360031b1b821691505b50919050565b634e487b7160e01b600052603260045260246000fd5b60006001820161049d57634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b60805160a05160c05160e05161010051610120516101405161016051613a05610549600039600081816106050152610f9b01526000818161040c01528181611a2c0152611a5b0152600081816105cb01526114ce01526000610de20152600061047201526000818161089d01528181610ce6015261143501526000610a0b015260006109db0152613a056000f3fe608060405234801561001057600080fd5b50600436106103365760003560e01c806381399be4116101b2578063a9059cbb116100f9578063d505accf116100a2578063e3ee160e1161007c578063e3ee160e1461082f578063e94a010214610842578063ef55bec614610870578063fc387d5a1461088357600080fd5b8063d505accf146107ca578063d9169487146107dd578063dd62ed3e1461080457600080fd5b8063c634dfaa116100d3578063c634dfaa14610791578063ce5494bb146107a4578063cf092995146107b757600080fd5b8063a9059cbb14610758578063ace150a51461076b578063b7b728991461077e57600080fd5b806395d89b411161015b578063a08cb48b11610135578063a08cb48b14610716578063a0cc6a6814610729578063a36e40fc1461075057600080fd5b806395d89b41146106f35780639f8495f9146106fb5780639fd5a6cf1461070357600080fd5b806388b7ab631161018c57806388b7ab63146106d05780638a75f238146106e35780638fd3ab80146106eb57600080fd5b806381399be41461067657806384af270f1461068957806384b0196e146106b557600080fd5b8063499d1081116102815780635c60da1b1161022a57806374df25581161020457806374df2558146106005780637ecebe00146106275780637f2eecc3146106475780638129fc1c1461066e57600080fd5b80635c60da1b1461059f5780636425666b146105c657806370a08231146105ed57600080fd5b806353d96f2c1161025b57806353d96f2c14610535578063578f2aa0146105705780635a049a701461058c57600080fd5b8063499d1081146104d65780634c57a8fa146104e9578063532992c51461052257600080fd5b80632b20e397116102e35780633644e515116102bd5780633644e515146104a657806340c10f19146104ae57806342966c68146104c357600080fd5b80632b20e3971461040757806330adf81f14610446578063313ce5671461046d57600080fd5b806323b872dd1161031457806323b872dd1461039257806326987b60146103a5578063281b229d146103dc57600080fd5b806306fdde031461033b578063095ea7b31461035957806318160ddd1461037c575b600080fd5b610343610896565b6040516103509190613217565b60405180910390f35b61036c610367366004613246565b6108c6565b6040519015158152602001610350565b6103846108dc565b604051908152602001610350565b61036c6103a0366004613270565b6108fe565b6000546fffffffffffffffffffffffffffffffff165b6040516fffffffffffffffffffffffffffffffff9091168152602001610350565b6004546103ef906001600160f01b031681565b6040516001600160f01b039091168152602001610350565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610350565b6103847f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104947f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610350565b6103846109d7565b6104c16104bc366004613246565b610a2d565b005b6104c16104d13660046132ad565b610a43565b6104c16104e43660046132e6565b610a58565b600554610503906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610350565b6104c1610530366004613322565b610a78565b60005461055a90700100000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610350565b6000546103bb906fffffffffffffffffffffffffffffffff1681565b6104c161059a36600461336c565b610a9d565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5461042e565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6103846105fb3660046133ba565b610ac4565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6103846106353660046133ba565b60016020526000908152604090205481565b6103847fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104c1610b23565b6104c16106843660046133ba565b610c8c565b61036c6106973660046133ba565b6001600160a01b031660009081526006602052604090205460ff1690565b6106bd610cd5565b60405161035097969594939291906133d5565b6104c16106de366004613584565b610d81565b6103ef610db0565b6104c1610dcf565b610343610ddb565b6104c1610e06565b6104c1610711366004613609565b610e0f565b6104c161072436600461367b565b610e1f565b6103847f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104c1610e49565b61036c610766366004613246565b610efb565b6104c161077936600461367b565b610f08565b6104c161078c3660046136e2565b610f28565b6103ef61079f3660046133ba565b610f40565b6104c16107b23660046133ba565b610f90565b6104c16107c5366004613584565b610ffb565b6104c16107d8366004613739565b61101b565b6103847f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103846108123660046137a4565b600360209081526000928352604080842090915290825290205481565b6104c161083d3660046137d7565b61102b565b61036c610850366004613246565b600260209081526000928352604080842090915290825290205460ff1681565b6104c161087e3660046137d7565b611056565b6104c1610891366004613858565b611076565b60606108c17f0000000000000000000000000000000000000000000000000000000000000000611087565b905090565b60006108d333848461117c565b50600192915050565b60006108e6610db0565b6004546001600160f01b039081169190910116919050565b6001600160a01b03831660009081526003602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109c15782811015610998576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b6001600160a01b0385166000908152600360209081526040808320338452909152902083820390555b6109cc8585856111dd565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610a08576108c161140d565b507f000000000000000000000000000000000000000000000000000000000000000090565b610a356114c3565b610a3f8282611525565b5050565b610a4b6114c3565b610a5533826116da565b50565b610a606114c3565b610a698161176c565b610a738383611525565b505050565b610a8d84610a868686611864565b84846118d3565b610a9784846118e7565b50505050565b610ab385610aab8787611864565b858585611969565b610abd85856118e7565b5050505050565b6001600160a01b0381166000908152600660205260408120805460ff16610afa57805461010090046001600160f01b0316610b13565b8054610b139061010090046001600160f01b0316611979565b6001600160f01b03169392505050565b6000610b2d6119a5565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610b5a5750825b905060008267ffffffffffffffff166001148015610b775750303b155b905081158015610b85575080155b15610bbc576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610c1d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610c256119ce565b8315610abd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b610c9581611a25565b15610ccc576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611a80565b600060608060008060006060610d0a7f0000000000000000000000000000000000000000000000000000000000000000611087565b6040805160008082526060820183526001602083019081527f3100000000000000000000000000000000000000000000000000000000000000938301939093527f0f000000000000000000000000000000000000000000000000000000000000009b939a5091985046975030965090945092509050565b610d9987610d93898989898989611c26565b83611ca1565b610da7878787878787611cee565b50505050505050565b6005546000906108c1906dffffffffffffffffffffffffffff16611979565b610dd96000611d57565b565b60606108c17f0000000000000000000000000000000000000000000000000000000000000000611087565b610dd933611a80565b610abd85610d9387878787611f41565b610e3188610a868a8a8a8a8a8a611c26565b610e3f888888888888611cee565b5050505050505050565b610e5233611a25565b610e88576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64e8d4a51000610ea96000546fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1603610ef2576040517f5dcd0a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd933611fdc565b60006108d33384846111dd565b610f1a88610a868a8a8a8a8a8a61215b565b610e3f8888888888886121cb565b610f3683610d938585611864565b610a7383836118e7565b6001600160a01b0381166000908152600660205260408120805460ff16610f68576000610f79565b805461010090046001600160f01b03165b6dffffffffffffffffffffffffffff169392505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610ff2576040517f51e2625e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611d57565b61100d87610d9389898989898961215b565b610da78787878787876121cb565b610da787610aab89898989611f41565b61103d89610aab8b8b8b8b8b8b61215b565b61104b8989898989896121cb565b505050505050505050565b61106889610aab8b8b8b8b8b8b611c26565b61104b898989898989611cee565b61107e6114c3565b610a558161176c565b606060005b6020811080156110ad57508281602081106110a9576110a9613873565b1a15155b156110c2576110bb816138a2565b905061108c565b60008167ffffffffffffffff8111156110dd576110dd613489565b6040519080825280601f01601f191660200182016040528015611107576020820181803683370190505b50905060005b828110156111745784816020811061112757611127613873565b1a60f81b82828151811061113d5761113d613873565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060010161110d565b509392505050565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6111e6826122d8565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161122b91815260200190565b60405180910390a3600061123e82612323565b6001600160a01b0380861660009081526006602052604080822054928716825290205491925060ff90811691161515811515036112a357610abd8585836112855784612390565b61128e8561236a565b6dffffffffffffffffffffffffffff16612390565b801561134f576112bb856112b68461236a565b612492565b6001600160a01b038416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168601811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055610abd565b61135985836125d4565b610abd84611366846126f1565b6001600160a01b0391909116600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490046001600160f01b03908116919091011690920217909155600580547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6114597f0000000000000000000000000000000000000000000000000000000000000000611087565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610dd9576040517f3f94bdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61152e81612717565b611537826122d8565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3600061158382612323565b6004549091506001600160f01b039081168183160111806115e157506004546dffffffffffffffffffffffffffff906115c6906001600160f01b0316830161236a565b6005546dffffffffffffffffffffffffffff90811691160110155b15611618576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526006602052604090205460ff161561164657610a7383611366836126f1565b6001600160a01b038316600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168501811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b6116e381612717565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36001600160a01b03821660009081526006602052604090205460ff161561175a57610a3f826112b661175584612323565b61236a565b610a3f8261176783612323565b6125d4565b6000546fffffffffffffffffffffffffffffffff90811690821610156117de576000546040517fecb4124f0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8084166004830152909116602482015260440161098f565b600080546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffff00000000000000000000000000000000000000000090911681177001000000000000000000000000000000004264ffffffffff160217825560405190917fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce267191a250565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03841691810191909152606081018290526000906118cc906080015b60405160208183030381529060405280519060200120612754565b9392505050565b610a976118e2858585856127b6565b6127f4565b6118f182826129b9565b6001600160a01b038216600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610abd6118e28686868686612a29565b600061199f8261199a6000546fffffffffffffffffffffffffffffffff1690565b612a74565b92915050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061199f565b6119d6612a80565b6000805464ffffffffff4216700100000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009091161764e8d4a51000179055565b6000611a507f0000000000000000000000000000000000000000000000000000000000000000612abe565b8061199f575061199f7f000000000000000000000000000000000000000000000000000000000000000083612af3565b6001600160a01b0381166000908152600660205260409020805460ff16611aa5575050565b6040516001600160a01b038316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681556001600160a01b03821660009081526006602052604081205461010090046001600160f01b038116916dffffffffffffffffffffffffffff9091169003611b4557505050565b6000611b5082611979565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166101006001600160f01b038088169190910291909117909155600480547fffff000000000000000000000000000000000000000000000000000000000000811690831690960190911694909417909355600580547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff91821693909303169190911790555050565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b979650505050505050565b6000611cae848484612b20565b90506000816005811115611cc457611cc4613901565b03611ccf5750505050565b611cda848484612b67565b15611ce55750505050565b610a97816127f4565b336001600160a01b03861614611d41576040517f1c5939f30000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b038616602482015260440161098f565b611d4f8686868686866121cb565b505050505050565b6001600160a01b038116611d97576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611ddb576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e057f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611e51576040519150601f19603f3d011682016040523d82523d6000602084013e611e56565b606091505b5050905080611e91576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ebb7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b6000611f4c82612ca7565b611f5785858561117c565b6001600160a01b038581166000818152600160208181526040928390208054928301905582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918101919091529182019290925291861660608301526080820185905260a082015260c08101839052611fd39060e0016118b1565b95945050505050565b6001600160a01b0381166000908152600660205260409020805460ff1615612002575050565b6040516001600160a01b038316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082556001600160f01b0361010090910416600081900361207d57505050565b6000612088826126f1565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8087166101000291909117909155600580547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831690960190911694909417909355600480547fffff00000000000000000000000000000000000000000000000000000000000081166001600160f01b0391821693909303169190911790555050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b82421161220d576040517f498ff9a20000000000000000000000000000000000000000000000000000000081524260048201526024810184905260440161098f565b81421061224f576040517fb3fcd33e0000000000000000000000000000000000000000000000000000000081524260048201526024810183905260440161098f565b61225986826129b9565b6001600160a01b038616600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3611d4f8686866111dd565b6001600160a01b038116610a55576040517f17858bbe0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161098f565b60006001600160f01b03821115612366576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600061199f8261238b6000546fffffffffffffffffffffffffffffffff1690565b612cea565b6001600160a01b0383166000908152600660205260409020546001600160f01b036101009091048116908216811015612416576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b0392831660009081526006602052604080822080546001600160f01b036101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b6001600160a01b03821660009081526006602052604090205461010090046001600160f01b03166dffffffffffffffffffffffffffff821681101561252b576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526dffffffffffffffffffffffffffff8316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546dffffffffffffffffffffffffffff8084166001600160f01b0361010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560058054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b6001600160a01b0382166000908152600660205260409020546001600160f01b03610100909104811690821681101561265a576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546001600160f01b0361010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560048054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b600061199f826127126000546fffffffffffffffffffffffffffffffff1690565b612cf6565b80600003610a55576040517f77b8dde30000000000000000000000000000000000000000000000000000000081526004810182905260240161098f565b600061275e6109d7565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b60008060006127c6868686612d02565b909250905060008260058111156127df576127df613901565b146127ea5781611c96565b611c968782612d4a565b600081600581111561280857612808613901565b036128105750565b600181600581111561282457612824613901565b0361285b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281600581111561286f5761286f613901565b036128a6576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038160058111156128ba576128ba613901565b036128f1576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561290557612905613901565b0361293c576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581600581111561295057612950613901565b03612987576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260026020908152604080832084845290915290205460ff1615610a3f576040517fd309466d0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161098f565b6000806000612a3a87878787612d75565b90925090506000826005811115612a5357612a53613901565b14612a5e5781612a68565b612a688882612d4a565b98975050505050505050565b60006118cc8383612e7d565b612a88612eb0565b610dd9576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612aeb837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612ecf565b141592915050565b60006118cc837f6561726e6572730000000000000000000000000000000000000000000000000084612f54565b6000806000612b2f8585612fea565b90925090506000826005811115612b4857612b48613901565b14612b535781612b5d565b612b5d8682612d4a565b9695505050505050565b6000806000856001600160a01b03168585604051602401612b89929190613930565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612c0a9190613949565b600060405180830381855afa9150503d8060008114612c45576040519150601f19603f3d011682016040523d82523d6000602084013e612c4a565b606091505b5091509150818015612c5e57506020815110155b8015612b5d575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612c9c9083016020908101908401613965565b149695505050505050565b80421115610a55576040517ff88f04900000000000000000000000000000000000000000000000000000000081526004810182905242602482015260440161098f565b60006118cc8383613031565b60006118cc83836130dd565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416612d3c87838884612d75565b935093505050935093915050565b6000816001600160a01b0316836001600160a01b031614612d6c5760056118cc565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612dac5750600390506000612e74565b8460ff16601b14158015612dc457508460ff16601c14155b15612dd55750600490506000612e74565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612e28573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615612e6a57600081612e6f565b600160005b915091505b94509492505050565b600064e8d4a510006fffffffffffffffffffffffffffffffff83166dffffffffffffffffffffffffffff851602046118cc565b6000612eba6119a5565b5468010000000000000000900460ff16919050565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690638eaa6ac090602401602060405180830381865afa158015612f30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc9190613965565b6040517fd7d1c1c0000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301526000919085169063d7d1c1c090604401602060405180830381865afa158015612fbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fe2919061397e565b949350505050565b6000808251604114613002575060029050600061302a565b60208301516040840151606085015160001a919061302287848484612d75565b945094505050505b9250929050565b6000816fffffffffffffffffffffffffffffffff1660000361307f576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160f01b03861664e8d4a5100002820101816130d7576130d76139a0565b0461315f565b6000816fffffffffffffffffffffffffffffffff1660000361312b576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83166001600160f01b03851664e8d4a5100002816130d7576130d76139a0565b60006dffffffffffffffffffffffffffff821115612366576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156131c45781810151838201526020016131ac565b50506000910152565b600081518084526131e58160208601602086016131a9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006118cc60208301846131cd565b80356001600160a01b038116811461324157600080fd5b919050565b6000806040838503121561325957600080fd5b6132628361322a565b946020939093013593505050565b60008060006060848603121561328557600080fd5b61328e8461322a565b925061329c6020850161322a565b929592945050506040919091013590565b6000602082840312156132bf57600080fd5b5035919050565b80356fffffffffffffffffffffffffffffffff8116811461324157600080fd5b6000806000606084860312156132fb57600080fd5b6133048461322a565b925060208401359150613319604085016132c6565b90509250925092565b6000806000806080858703121561333857600080fd5b6133418561322a565b966020860135965060408601359560600135945092505050565b803560ff8116811461324157600080fd5b600080600080600060a0868803121561338457600080fd5b61338d8661322a565b9450602086013593506133a26040870161335b565b94979396509394606081013594506080013592915050565b6000602082840312156133cc57600080fd5b6118cc8261322a565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061341060e08301896131cd565b828103604084015261342281896131cd565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b8181101561347857835183526020938401939092019160010161345a565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126134c957600080fd5b813567ffffffffffffffff8111156134e3576134e3613489565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561354f5761354f613489565b60405281815283820160200185101561356757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561359f57600080fd5b6135a88861322a565b96506135b66020890161322a565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156135ee57600080fd5b6135fa8a828b016134b8565b91505092959891949750929550565b600080600080600060a0868803121561362157600080fd5b61362a8661322a565b94506136386020870161322a565b93506040860135925060608601359150608086013567ffffffffffffffff81111561366257600080fd5b61366e888289016134b8565b9150509295509295909350565b600080600080600080600080610100898b03121561369857600080fd5b6136a18961322a565b97506136af60208a0161322a565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b6000806000606084860312156136f757600080fd5b6137008461322a565b925060208401359150604084013567ffffffffffffffff81111561372357600080fd5b61372f868287016134b8565b9150509250925092565b600080600080600080600060e0888a03121561375457600080fd5b61375d8861322a565b965061376b6020890161322a565b955060408801359450606088013593506137876080890161335b565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156137b757600080fd5b6137c08361322a565b91506137ce6020840161322a565b90509250929050565b60008060008060008060008060006101208a8c0312156137f657600080fd5b6137ff8a61322a565b985061380d60208b0161322a565b975060408a0135965060608a0135955060808a0135945060a08a0135935061383760c08b0161335b565b989b979a50959894979396929550929360e081013593506101000135919050565b60006020828403121561386a57600080fd5b6118cc826132c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036138fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000612fe260408301846131cd565b6000825161395b8184602087016131a9565b9190910192915050565b60006020828403121561397757600080fd5b5051919050565b60006020828403121561399057600080fd5b815180151581146118cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df783e511c5dd57d9bbd494eb2b5b0a7ccdefc1e768a2e63da033ca5cf1c205a64736f6c634300081a0033000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103365760003560e01c806381399be4116101b2578063a9059cbb116100f9578063d505accf116100a2578063e3ee160e1161007c578063e3ee160e1461082f578063e94a010214610842578063ef55bec614610870578063fc387d5a1461088357600080fd5b8063d505accf146107ca578063d9169487146107dd578063dd62ed3e1461080457600080fd5b8063c634dfaa116100d3578063c634dfaa14610791578063ce5494bb146107a4578063cf092995146107b757600080fd5b8063a9059cbb14610758578063ace150a51461076b578063b7b728991461077e57600080fd5b806395d89b411161015b578063a08cb48b11610135578063a08cb48b14610716578063a0cc6a6814610729578063a36e40fc1461075057600080fd5b806395d89b41146106f35780639f8495f9146106fb5780639fd5a6cf1461070357600080fd5b806388b7ab631161018c57806388b7ab63146106d05780638a75f238146106e35780638fd3ab80146106eb57600080fd5b806381399be41461067657806384af270f1461068957806384b0196e146106b557600080fd5b8063499d1081116102815780635c60da1b1161022a57806374df25581161020457806374df2558146106005780637ecebe00146106275780637f2eecc3146106475780638129fc1c1461066e57600080fd5b80635c60da1b1461059f5780636425666b146105c657806370a08231146105ed57600080fd5b806353d96f2c1161025b57806353d96f2c14610535578063578f2aa0146105705780635a049a701461058c57600080fd5b8063499d1081146104d65780634c57a8fa146104e9578063532992c51461052257600080fd5b80632b20e397116102e35780633644e515116102bd5780633644e515146104a657806340c10f19146104ae57806342966c68146104c357600080fd5b80632b20e3971461040757806330adf81f14610446578063313ce5671461046d57600080fd5b806323b872dd1161031457806323b872dd1461039257806326987b60146103a5578063281b229d146103dc57600080fd5b806306fdde031461033b578063095ea7b31461035957806318160ddd1461037c575b600080fd5b610343610896565b6040516103509190613217565b60405180910390f35b61036c610367366004613246565b6108c6565b6040519015158152602001610350565b6103846108dc565b604051908152602001610350565b61036c6103a0366004613270565b6108fe565b6000546fffffffffffffffffffffffffffffffff165b6040516fffffffffffffffffffffffffffffffff9091168152602001610350565b6004546103ef906001600160f01b031681565b6040516001600160f01b039091168152602001610350565b61042e7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b6040516001600160a01b039091168152602001610350565b6103847f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104947f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff9091168152602001610350565b6103846109d7565b6104c16104bc366004613246565b610a2d565b005b6104c16104d13660046132ad565b610a43565b6104c16104e43660046132e6565b610a58565b600554610503906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610350565b6104c1610530366004613322565b610a78565b60005461055a90700100000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610350565b6000546103bb906fffffffffffffffffffffffffffffffff1681565b6104c161059a36600461336c565b610a9d565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5461042e565b61042e7f00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece81565b6103846105fb3660046133ba565b610ac4565b61042e7f000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb81565b6103846106353660046133ba565b60016020526000908152604090205481565b6103847fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104c1610b23565b6104c16106843660046133ba565b610c8c565b61036c6106973660046133ba565b6001600160a01b031660009081526006602052604090205460ff1690565b6106bd610cd5565b60405161035097969594939291906133d5565b6104c16106de366004613584565b610d81565b6103ef610db0565b6104c1610dcf565b610343610ddb565b6104c1610e06565b6104c1610711366004613609565b610e0f565b6104c161072436600461367b565b610e1f565b6103847f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104c1610e49565b61036c610766366004613246565b610efb565b6104c161077936600461367b565b610f08565b6104c161078c3660046136e2565b610f28565b6103ef61079f3660046133ba565b610f40565b6104c16107b23660046133ba565b610f90565b6104c16107c5366004613584565b610ffb565b6104c16107d8366004613739565b61101b565b6103847f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103846108123660046137a4565b600360209081526000928352604080842090915290825290205481565b6104c161083d3660046137d7565b61102b565b61036c610850366004613246565b600260209081526000928352604080842090915290825290205460ff1681565b6104c161087e3660046137d7565b611056565b6104c1610891366004613858565b611076565b60606108c17f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b905090565b60006108d333848461117c565b50600192915050565b60006108e6610db0565b6004546001600160f01b039081169190910116919050565b6001600160a01b03831660009081526003602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109c15782811015610998576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b6001600160a01b0385166000908152600360209081526040808320338452909152902083820390555b6109cc8585856111dd565b506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000e7084614610a08576108c161140d565b507f986055a5a77f4a884f055ed4659511cb4d51e6f4bf1b7688f26804571b2ccf2690565b610a356114c3565b610a3f8282611525565b5050565b610a4b6114c3565b610a5533826116da565b50565b610a606114c3565b610a698161176c565b610a738383611525565b505050565b610a8d84610a868686611864565b84846118d3565b610a9784846118e7565b50505050565b610ab385610aab8787611864565b858585611969565b610abd85856118e7565b5050505050565b6001600160a01b0381166000908152600660205260408120805460ff16610afa57805461010090046001600160f01b0316610b13565b8054610b139061010090046001600160f01b0316611979565b6001600160f01b03169392505050565b6000610b2d6119a5565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610b5a5750825b905060008267ffffffffffffffff166001148015610b775750303b155b905081158015610b85575080155b15610bbc576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610c1d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610c256119ce565b8315610abd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b610c9581611a25565b15610ccc576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611a80565b600060608060008060006060610d0a7f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b6040805160008082526060820183526001602083019081527f3100000000000000000000000000000000000000000000000000000000000000938301939093527f0f000000000000000000000000000000000000000000000000000000000000009b939a5091985046975030965090945092509050565b610d9987610d93898989898989611c26565b83611ca1565b610da7878787878787611cee565b50505050505050565b6005546000906108c1906dffffffffffffffffffffffffffff16611979565b610dd96000611d57565b565b60606108c17f4d00000000000000000000000000000000000000000000000000000000000000611087565b610dd933611a80565b610abd85610d9387878787611f41565b610e3188610a868a8a8a8a8a8a611c26565b610e3f888888888888611cee565b5050505050505050565b610e5233611a25565b610e88576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64e8d4a51000610ea96000546fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1603610ef2576040517f5dcd0a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd933611fdc565b60006108d33384846111dd565b610f1a88610a868a8a8a8a8a8a61215b565b610e3f8888888888886121cb565b610f3683610d938585611864565b610a7383836118e7565b6001600160a01b0381166000908152600660205260408120805460ff16610f68576000610f79565b805461010090046001600160f01b03165b6dffffffffffffffffffffffffffff169392505050565b336001600160a01b037f000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb1614610ff2576040517f51e2625e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611d57565b61100d87610d9389898989898961215b565b610da78787878787876121cb565b610da787610aab89898989611f41565b61103d89610aab8b8b8b8b8b8b61215b565b61104b8989898989896121cb565b505050505050505050565b61106889610aab8b8b8b8b8b8b611c26565b61104b898989898989611cee565b61107e6114c3565b610a558161176c565b606060005b6020811080156110ad57508281602081106110a9576110a9613873565b1a15155b156110c2576110bb816138a2565b905061108c565b60008167ffffffffffffffff8111156110dd576110dd613489565b6040519080825280601f01601f191660200182016040528015611107576020820181803683370190505b50905060005b828110156111745784816020811061112757611127613873565b1a60f81b82828151811061113d5761113d613873565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060010161110d565b509392505050565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6111e6826122d8565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161122b91815260200190565b60405180910390a3600061123e82612323565b6001600160a01b0380861660009081526006602052604080822054928716825290205491925060ff90811691161515811515036112a357610abd8585836112855784612390565b61128e8561236a565b6dffffffffffffffffffffffffffff16612390565b801561134f576112bb856112b68461236a565b612492565b6001600160a01b038416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168601811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055610abd565b61135985836125d4565b610abd84611366846126f1565b6001600160a01b0391909116600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490046001600160f01b03908116919091011690920217909155600580547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6114597f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b336001600160a01b037f00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece1614610dd9576040517f3f94bdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61152e81612717565b611537826122d8565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3600061158382612323565b6004549091506001600160f01b039081168183160111806115e157506004546dffffffffffffffffffffffffffff906115c6906001600160f01b0316830161236a565b6005546dffffffffffffffffffffffffffff90811691160110155b15611618576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526006602052604090205460ff161561164657610a7383611366836126f1565b6001600160a01b038316600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168501811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b6116e381612717565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36001600160a01b03821660009081526006602052604090205460ff161561175a57610a3f826112b661175584612323565b61236a565b610a3f8261176783612323565b6125d4565b6000546fffffffffffffffffffffffffffffffff90811690821610156117de576000546040517fecb4124f0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8084166004830152909116602482015260440161098f565b600080546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffff00000000000000000000000000000000000000000090911681177001000000000000000000000000000000004264ffffffffff160217825560405190917fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce267191a250565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03841691810191909152606081018290526000906118cc906080015b60405160208183030381529060405280519060200120612754565b9392505050565b610a976118e2858585856127b6565b6127f4565b6118f182826129b9565b6001600160a01b038216600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610abd6118e28686868686612a29565b600061199f8261199a6000546fffffffffffffffffffffffffffffffff1690565b612a74565b92915050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061199f565b6119d6612a80565b6000805464ffffffffff4216700100000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009091161764e8d4a51000179055565b6000611a507f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c612abe565b8061199f575061199f7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c83612af3565b6001600160a01b0381166000908152600660205260409020805460ff16611aa5575050565b6040516001600160a01b038316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681556001600160a01b03821660009081526006602052604081205461010090046001600160f01b038116916dffffffffffffffffffffffffffff9091169003611b4557505050565b6000611b5082611979565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166101006001600160f01b038088169190910291909117909155600480547fffff000000000000000000000000000000000000000000000000000000000000811690831690960190911694909417909355600580547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff91821693909303169190911790555050565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b979650505050505050565b6000611cae848484612b20565b90506000816005811115611cc457611cc4613901565b03611ccf5750505050565b611cda848484612b67565b15611ce55750505050565b610a97816127f4565b336001600160a01b03861614611d41576040517f1c5939f30000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b038616602482015260440161098f565b611d4f8686868686866121cb565b505050505050565b6001600160a01b038116611d97576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611ddb576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e057f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611e51576040519150601f19603f3d011682016040523d82523d6000602084013e611e56565b606091505b5050905080611e91576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ebb7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b6000611f4c82612ca7565b611f5785858561117c565b6001600160a01b038581166000818152600160208181526040928390208054928301905582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918101919091529182019290925291861660608301526080820185905260a082015260c08101839052611fd39060e0016118b1565b95945050505050565b6001600160a01b0381166000908152600660205260409020805460ff1615612002575050565b6040516001600160a01b038316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082556001600160f01b0361010090910416600081900361207d57505050565b6000612088826126f1565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8087166101000291909117909155600580547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831690960190911694909417909355600480547fffff00000000000000000000000000000000000000000000000000000000000081166001600160f01b0391821693909303169190911790555050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b82421161220d576040517f498ff9a20000000000000000000000000000000000000000000000000000000081524260048201526024810184905260440161098f565b81421061224f576040517fb3fcd33e0000000000000000000000000000000000000000000000000000000081524260048201526024810183905260440161098f565b61225986826129b9565b6001600160a01b038616600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3611d4f8686866111dd565b6001600160a01b038116610a55576040517f17858bbe0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161098f565b60006001600160f01b03821115612366576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600061199f8261238b6000546fffffffffffffffffffffffffffffffff1690565b612cea565b6001600160a01b0383166000908152600660205260409020546001600160f01b036101009091048116908216811015612416576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b0392831660009081526006602052604080822080546001600160f01b036101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b6001600160a01b03821660009081526006602052604090205461010090046001600160f01b03166dffffffffffffffffffffffffffff821681101561252b576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526dffffffffffffffffffffffffffff8316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546dffffffffffffffffffffffffffff8084166001600160f01b0361010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560058054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b6001600160a01b0382166000908152600660205260409020546001600160f01b03610100909104811690821681101561265a576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546001600160f01b0361010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560048054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b600061199f826127126000546fffffffffffffffffffffffffffffffff1690565b612cf6565b80600003610a55576040517f77b8dde30000000000000000000000000000000000000000000000000000000081526004810182905260240161098f565b600061275e6109d7565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b60008060006127c6868686612d02565b909250905060008260058111156127df576127df613901565b146127ea5781611c96565b611c968782612d4a565b600081600581111561280857612808613901565b036128105750565b600181600581111561282457612824613901565b0361285b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281600581111561286f5761286f613901565b036128a6576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038160058111156128ba576128ba613901565b036128f1576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561290557612905613901565b0361293c576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581600581111561295057612950613901565b03612987576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260026020908152604080832084845290915290205460ff1615610a3f576040517fd309466d0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161098f565b6000806000612a3a87878787612d75565b90925090506000826005811115612a5357612a53613901565b14612a5e5781612a68565b612a688882612d4a565b98975050505050505050565b60006118cc8383612e7d565b612a88612eb0565b610dd9576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612aeb837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612ecf565b141592915050565b60006118cc837f6561726e6572730000000000000000000000000000000000000000000000000084612f54565b6000806000612b2f8585612fea565b90925090506000826005811115612b4857612b48613901565b14612b535781612b5d565b612b5d8682612d4a565b9695505050505050565b6000806000856001600160a01b03168585604051602401612b89929190613930565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612c0a9190613949565b600060405180830381855afa9150503d8060008114612c45576040519150601f19603f3d011682016040523d82523d6000602084013e612c4a565b606091505b5091509150818015612c5e57506020815110155b8015612b5d575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612c9c9083016020908101908401613965565b149695505050505050565b80421115610a55576040517ff88f04900000000000000000000000000000000000000000000000000000000081526004810182905242602482015260440161098f565b60006118cc8383613031565b60006118cc83836130dd565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416612d3c87838884612d75565b935093505050935093915050565b6000816001600160a01b0316836001600160a01b031614612d6c5760056118cc565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612dac5750600390506000612e74565b8460ff16601b14158015612dc457508460ff16601c14155b15612dd55750600490506000612e74565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612e28573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615612e6a57600081612e6f565b600160005b915091505b94509492505050565b600064e8d4a510006fffffffffffffffffffffffffffffffff83166dffffffffffffffffffffffffffff851602046118cc565b6000612eba6119a5565b5468010000000000000000900460ff16919050565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690638eaa6ac090602401602060405180830381865afa158015612f30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc9190613965565b6040517fd7d1c1c0000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301526000919085169063d7d1c1c090604401602060405180830381865afa158015612fbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fe2919061397e565b949350505050565b6000808251604114613002575060029050600061302a565b60208301516040840151606085015160001a919061302287848484612d75565b945094505050505b9250929050565b6000816fffffffffffffffffffffffffffffffff1660000361307f576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160f01b03861664e8d4a5100002820101816130d7576130d76139a0565b0461315f565b6000816fffffffffffffffffffffffffffffffff1660000361312b576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83166001600160f01b03851664e8d4a5100002816130d7576130d76139a0565b60006dffffffffffffffffffffffffffff821115612366576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156131c45781810151838201526020016131ac565b50506000910152565b600081518084526131e58160208601602086016131a9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006118cc60208301846131cd565b80356001600160a01b038116811461324157600080fd5b919050565b6000806040838503121561325957600080fd5b6132628361322a565b946020939093013593505050565b60008060006060848603121561328557600080fd5b61328e8461322a565b925061329c6020850161322a565b929592945050506040919091013590565b6000602082840312156132bf57600080fd5b5035919050565b80356fffffffffffffffffffffffffffffffff8116811461324157600080fd5b6000806000606084860312156132fb57600080fd5b6133048461322a565b925060208401359150613319604085016132c6565b90509250925092565b6000806000806080858703121561333857600080fd5b6133418561322a565b966020860135965060408601359560600135945092505050565b803560ff8116811461324157600080fd5b600080600080600060a0868803121561338457600080fd5b61338d8661322a565b9450602086013593506133a26040870161335b565b94979396509394606081013594506080013592915050565b6000602082840312156133cc57600080fd5b6118cc8261322a565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061341060e08301896131cd565b828103604084015261342281896131cd565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b8181101561347857835183526020938401939092019160010161345a565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126134c957600080fd5b813567ffffffffffffffff8111156134e3576134e3613489565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561354f5761354f613489565b60405281815283820160200185101561356757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561359f57600080fd5b6135a88861322a565b96506135b66020890161322a565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156135ee57600080fd5b6135fa8a828b016134b8565b91505092959891949750929550565b600080600080600060a0868803121561362157600080fd5b61362a8661322a565b94506136386020870161322a565b93506040860135925060608601359150608086013567ffffffffffffffff81111561366257600080fd5b61366e888289016134b8565b9150509295509295909350565b600080600080600080600080610100898b03121561369857600080fd5b6136a18961322a565b97506136af60208a0161322a565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b6000806000606084860312156136f757600080fd5b6137008461322a565b925060208401359150604084013567ffffffffffffffff81111561372357600080fd5b61372f868287016134b8565b9150509250925092565b600080600080600080600060e0888a03121561375457600080fd5b61375d8861322a565b965061376b6020890161322a565b955060408801359450606088013593506137876080890161335b565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156137b757600080fd5b6137c08361322a565b91506137ce6020840161322a565b90509250929050565b60008060008060008060008060006101208a8c0312156137f657600080fd5b6137ff8a61322a565b985061380d60208b0161322a565b975060408a0135965060608a0135955060808a0135945060a08a0135935061383760c08b0161335b565b989b979a50959894979396929550929360e081013593506101000135919050565b60006020828403121561386a57600080fd5b6118cc826132c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036138fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000612fe260408301846131cd565b6000825161395b8184602087016131a9565b9190910192915050565b60006020828403121561397757600080fd5b5051919050565b60006020828403121561399057600080fd5b815180151581146118cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df783e511c5dd57d9bbd494eb2b5b0a7ccdefc1e768a2e63da033ca5cf1c205a64736f6c634300081a0033

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

000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb

-----Decoded View---------------
Arg [0] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [1] : portal_ (address): 0x36f586A30502AE3afb555b8aA4dCc05d233c2ecE
Arg [2] : migrationAdmin_ (address): 0xF2f1ACbe0BA726fEE8d75f3E32900526874740BB

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [1] : 00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece
Arg [2] : 000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

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