ETH Price: $2,235.49 (-4.69%)
 

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Approve1445334932025-12-02 8:56:0363 days ago1764665763IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000048840.00010043
Approve1434555022025-11-07 10:03:0188 days ago1762509781IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000549150.001
Approve1432909572025-11-03 14:38:1192 days ago1762180691IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000193280.00023249
Approve1431148102025-10-30 12:46:3796 days ago1761828397IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000283080.00017318
Approve1430283702025-10-28 12:45:1798 days ago1761655517IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000054980.00007239
Approve1430283062025-10-28 12:43:0998 days ago1761655389IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000062350.00009057
Approve1419626392025-10-03 20:40:55123 days ago1759524055IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000023660.00000108
Approve1419626272025-10-03 20:40:31123 days ago1759524031IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000023390.0000012
Approve1401267922025-08-22 8:46:01165 days ago1755852361IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000067990.00010041
Approve1401267702025-08-22 8:45:17165 days ago1755852317IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000070070.00010042
Approve1396543492025-08-11 10:17:55176 days ago1754907475IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000087450.00010039
Approve1391850862025-07-31 13:35:49187 days ago1753968949IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000504220.00010055
Approve1380154342025-07-04 11:47:25214 days ago1751629645IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000004658110.0100032
Approve1380128042025-07-04 10:19:45214 days ago1751624385IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000004659980.01000225
Approve1379263722025-07-02 10:18:41216 days ago1751451521IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000114160.00011004
Approve1367586522025-06-05 9:34:41243 days ago1749116081IN
0x866A2BF4...Bfb36be1b
0 ETH0.000000015980.00010084
Approve1349468312025-04-24 11:00:39285 days ago1745492439IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000002693090.00087449
Approve1349395332025-04-24 6:57:23285 days ago1745477843IN
0x866A2BF4...Bfb36be1b
0 ETH0.000000243480.00011522
Approve1348757692025-04-22 19:31:55287 days ago1745350315IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000006085310.0010273
Approve1328818142025-03-07 15:46:45333 days ago1741362405IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000010787990.00226632
Approve1328816772025-03-07 15:42:11333 days ago1741362131IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000009387630.0022326
Approve1325368812025-02-27 16:08:59341 days ago1740672539IN
0x866A2BF4...Bfb36be1b
0 ETH0.000000576530.00361534
Approve1323968692025-02-24 10:21:55344 days ago1740392515IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000333750.0004644
Approve1323967762025-02-24 10:18:49344 days ago1740392329IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000404770.00060812
Approve1323967642025-02-24 10:18:25344 days ago1740392305IN
0x866A2BF4...Bfb36be1b
0 ETH0.0000000536620.00146364
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

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 1500 runs

Other Settings:
cancun EvmVersion
// 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 { 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 {
    /* ============ 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
    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.
     * @param  registrar_ The address of the Registrar contract.
     */
    constructor(address registrar_) ContinuousIndexing() ERC20Extended("M by M^0", "M", 6) {
        if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
        if ((portal = RegistrarReader.getPortal(registrar_)) == address(0)) revert ZeroPortal();
    }

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

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

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.26;

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.26;

/**
 * @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 given uint256 value to a uint16,
     *         ensuring that it is less than or equal to the maximum uint16 value.
     * @param  n The value to check.
     * @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 given uint256 value to a uint40,
     *         ensuring that it is less than or equal to the maximum uint40 value.
     * @param  n The value to check.
     * @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 given uint256 value to a uint48,
     *         ensuring that it is less than or equal to the maximum uint48 value.
     * @param  n The value to check.
     * @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 given uint256 value to a uint112,
     *         ensuring that it is less than or equal to the maximum uint112 value.
     * @param  n The value to check.
     * @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 given uint256 value to a uint128,
     *         ensuring that it is less than or equal to the maximum uint128 value.
     * @param  n The value to check.
     * @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 given uint256 value to a uint240,
     *         ensuring that it is less than or equal to the maximum uint240 value.
     * @param  n The value to check.
     * @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 given uint256 value to the maximum uint32 value.
     * @param  n The value to check.
     * @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 given uint256 value to the maximum uint112 value.
     * @param  n The value to check.
     * @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 given uint256 value to the maximum uint128 value.
     * @param  n The value to check.
     * @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 given uint256 value to the maximum uint240 value.
     * @param  n The value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @return The larger value.
     */
    function max40(uint40 a_, uint40 b_) internal pure returns (uint40) {
        return a_ > b_ ? a_ : b_;
    }

    /**
     * @notice Compares two uint32 values and returns the lesser one.
     * @param  a_  Value to check.
     * @param  b_  Value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @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 check.
     * @param  b_  Value to check.
     * @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.26;

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

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

    /* ============ 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 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 { 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 {
    /* ============ Variables ============ */

    /// @inheritdoc IContinuousIndexing
    uint128 public latestIndex;

    /// @inheritdoc IContinuousIndexing
    uint40 public latestUpdateTimestamp;

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

    /// @notice Constructs the ContinuousIndexing contract.
    constructor() {
        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.26;

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 block number 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 block number 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.26;

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

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

pragma solidity 0.8.26;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.26;

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.26;

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.26;

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.26;

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.26;

/**
 * @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 deadline of the signature.
     * @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.26;

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.26;

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

Settings
{
  "remappings": [
    "common/=lib/common/src/",
    "ds-test/=lib/native-token-transfers/evm/lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "native-token-transfers/=lib/native-token-transfers/",
    "openzeppelin-contracts/=lib/native-token-transfers/evm/lib/openzeppelin-contracts/",
    "protocol/=lib/protocol/",
    "solidity-bytes-utils/=lib/native-token-transfers/evm/lib/solidity-bytes-utils/contracts/",
    "ttg/=lib/ttg/",
    "wormhole-solidity-sdk/=lib/native-token-transfers/evm/lib/wormhole-solidity-sdk/src/",
    "wrapped-m-token/=lib/wrapped-m-token/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1500
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {
    "lib/native-token-transfers/evm/src/libraries/TransceiverStructs.sol": {
      "TransceiverStructs": "0x188B1E9E77B04E1699A78D929568Ed5BB15F962C"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"registrar_","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":[{"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":"NotApprovedEarner","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":"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":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"},{"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":[{"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":[{"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"}]

61016080604052346102f65761002b90612a85803803809161002182856102fa565b8339810190610331565b60409081519161003b81846102fa565b600883526704d206279204d5e360c41b602084015261009881519361006083866102fa565b60018552604d60f81b60208601525f80546001600160a81b0319164260801b64ffffffffff60801b161764e8d4a5100017905561036b565b918260c052466080525f5b60208110806102e7575b156100d6575f1981146100c2576001016100a3565b634e487b7160e01b5f52601160045260245ffd5b84906100e181610350565b6100ed855191826102fa565b8181526100f982610350565b602082019290601f19013684375f5b8181106102ae575050906101949291519020845160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8352868201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261018960c0826102fa565b51902060a05261036b565b61010052600660e0526101408190526001600160a01b0316801561029f576020600491835192838092636425666b60e01b82525afa908115610295575f91610266575b506101208190526001600160a01b03161561025757516126cf90816103b68239608051816110c9015260a051816110ef015260c05181818161077301528181610ea70152611116015260e05181610c6901526101005181610699015261012051818181610a260152611544015261014051818181610ce601526118a50152f35b631b973f8d60e11b5f5260045ffd5b610288915060203d60201161028e575b61028081836102fa565b810190610331565b826101d7565b503d610276565b82513d5f823e3d90fd5b6379a6314960e01b5f5260045ffd5b60208110156102d35782518110156102d35780886001921a6020828601015301610108565b634e487b7160e01b5f52603260045260245ffd5b156102d35783811a15156100ad565b5f80fd5b601f909101601f19168101906001600160401b0382119082101761031d57604052565b634e487b7160e01b5f52604160045260245ffd5b908160209103126102f657516001600160a01b03811681036102f65790565b6001600160401b03811161031d57601f01601f191660200190565b6040516103976020828180820195805191829101875e81015f838201520301601f1981018352826102fa565b51905190602081106103a7575090565b5f199060200360031b1b169056fe60806040526004361015610011575f80fd5b5f3560e01c806306fdde0314610e8f578063095ea7b314610e6957806318160ddd14610e0757806323b872dd14610d3057806326987b6014610a8d578063281b229d14610d0a5780632b20e39714610cc757806330adf81f14610c8d578063313ce56714610c505780633644e51514610c2e57806340c10f1914610c0a57806342966c6814610b76578063499d108114610b315780634c57a8fa14610b0b578063532992c514610ad857806353d96f2c14610ab2578063578f2aa014610a8d5780635a049a7014610a4a5780636425666b14610a0757806370a08231146109825780637ecebe001461094a5780637f2eecc31461091057806381399be4146108b957806384af270f1461087c57806384b0196e1461075b57806388b7ab63146107315780638a75f238146106d557806395d89b41146106815780639f8495f9146106685780639fd5a6cf1461060b578063a08cb48b146105e0578063a0cc6a68146105a6578063a36e40fc14610519578063a9059cbb146104e8578063ace150a5146104b7578063b7b7289914610465578063c634dfaa14610403578063cf092995146103d3578063d505accf14610380578063d916948714610346578063dd62ed3e146102f2578063e3ee160e146102c2578063e94a010214610279578063ef55bec6146102435763fc387d5a14610208575f80fd5b3461023f57602036600319011261023f576004356001600160801b038116810361023f5761023d9061023861153a565b6116b5565b005b5f80fd5b3461023f5761023d61027461025736611068565b9161026e8486888a8c989e988e9a9e9d9b9d611b4a565b8561188f565b611cbf565b3461023f57604036600319011261023f576001600160a01b0361029a610eef565b165f52600260205260405f206024355f52602052602060ff60405f2054166040519015158152f35b3461023f5761023d6102ed6102d636611068565b9161026e8486888a8c989e988e9a9e9d9b9d611ef7565b611f64565b3461023f57604036600319011261023f5761030b610eef565b6001600160a01b0361031b610f05565b91165f5260036020526001600160a01b0360405f2091165f52602052602060405f2054604051908152f35b3461023f575f36600319011261023f5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b3461023f5760e036600319011261023f57610399610eef565b6103a1610f05565b906084359160ff8316830361023f5761023d9260c435926103cd60a43593606435906044359084611d12565b9061188f565b3461023f5761023d6102ed6103e736610fb3565b6103fd828486888a8c989c9d979d9b999b611ef7565b83611bb7565b3461023f57602036600319011261023f576001600160a01b03610424610eef565b165f52600660205260405f205460ff81165f1461045c576001600160701b0360209160081c165b6001600160701b0360405191168152f35b5060205f61044b565b3461023f57606036600319011261023f5761047e610eef565b6024356044359167ffffffffffffffff831161023f576104b26104a861023d943690600401610f6d565b6103fd8484611765565b611834565b3461023f5761023d6102ed6104cb36611019565b906104e2838587898b8d999d9e989e9c9a9c611ef7565b846117bf565b3461023f57604036600319011261023f5761050e610504610eef565b60243590336112de565b602060405160018152f35b3461023f575f36600319011261023f576105323361189b565b1561057e5764e8d4a510006001600160801b035f5416146105565761023d33611dda565b7f5dcd0a37000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461023f575f36600319011261023f5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b3461023f5761023d6102746105f436611019565b906104e2838587898b8d999d9e989e9c9a9c611b4a565b3461023f5760a036600319011261023f57610624610eef565b61062c610f05565b6084359167ffffffffffffffff831161023f5761066261065361023d943690600401610f6d565b92606435906044359084611d12565b90611bb7565b3461023f575f36600319011261023f5761023d33611a26565b3461023f575f36600319011261023f576106d16106bd7f00000000000000000000000000000000000000000000000000000000000000006111b8565b604051918291602083526020830190610ecb565b0390f35b3461023f575f36600319011261023f5760206107206001600160701b036005541664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b6001600160f01b0360405191168152f35b3461023f5761023d61027461074536610fb3565b6103fd828486888a8c989c9d979d9b999b611b4a565b3461023f575f36600319011261023f576108206107977f00000000000000000000000000000000000000000000000000000000000000006111b8565b60405160206107a68183610f1b565b5f82525f36813761082e6040516107be604082610f1b565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e0870190610ecb565b908582036040870152610ecb565b4660608501523060808501525f60a085015283810360c08501528180845192838152019301915f5b82811061086557505050500390f35b835185528695509381019392810192600101610856565b3461023f57602036600319011261023f576001600160a01b0361089d610eef565b165f526006602052602060ff60405f2054166040519015158152f35b3461023f57602036600319011261023f576108d2610eef565b6108db8161189b565b6108e85761023d90611a26565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461023f575f36600319011261023f5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b3461023f57602036600319011261023f576001600160a01b0361096b610eef565b165f526001602052602060405f2054604051908152f35b3461023f57602036600319011261023f576001600160a01b036109a3610eef565b165f526006602052602060405f205460ff81165f146109f6576001600160701b036107209160081c1664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b60081c6001600160f01b0316610720565b3461023f575f36600319011261023f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023f5760a036600319011261023f57610a63610eef565b6024356044359160ff8316830361023f576104b261023d93608435906064359061026e8686611765565b3461023f575f36600319011261023f5760206001600160801b035f5416604051908152f35b3461023f575f36600319011261023f57602064ffffffffff5f5460801c16604051908152f35b3461023f57608036600319011261023f5761023d610af4610eef565b602435906104b26064356044356104e28585611765565b3461023f575f36600319011261023f5760206001600160701b0360055416604051908152f35b3461023f57606036600319011261023f57610b4a610eef565b604435906001600160801b038216820361023f57610b6d61023d9261023861153a565b60243590611594565b3461023f57602036600319011261023f57600435610b9261153a565b610b9b81612376565b5f6040518281527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203392a3335f52600660205260ff60405f2054165f14610bf857610bf2610bed61023d9261207b565b6120bd565b33612237565b610c0461023d9161207b565b336120f1565b3461023f57604036600319011261023f5761023d610c26610eef565b610b6d61153a565b3461023f575f36600319011261023f576020610c486110c6565b604051908152f35b3461023f575f36600319011261023f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023f575f36600319011261023f5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b3461023f575f36600319011261023f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023f575f36600319011261023f5760206001600160f01b0360045416604051908152f35b3461023f57606036600319011261023f57610d49610eef565b610d51610f05565b604435906001600160a01b0383165f52600360205260405f206001600160a01b0333165f5260205260405f2054925f198403610d92575b61050e93506112de565b828410610dd357610dce8361050e950333836001600160a01b03165f5260036020526001600160a01b0360405f2091165f5260205260405f2055565b610d88565b82847f192b9e4e000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461023f575f36600319011261023f5760206001600160f01b038060045416610e5f6001600160701b036005541664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b0116604051908152f35b3461023f57604036600319011261023f5761050e610e85610eef565b6024359033611272565b3461023f575f36600319011261023f576106d16106bd7f00000000000000000000000000000000000000000000000000000000000000006111b8565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361023f57565b602435906001600160a01b038216820361023f57565b90601f8019910116810190811067ffffffffffffffff821117610f3d57604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610f3d57601f01601f191660200190565b81601f8201121561023f57803590610f8482610f51565b92610f926040519485610f1b565b8284526020838301011161023f57815f926020809301838601378301015290565b60e060031982011261023f576004356001600160a01b038116810361023f57916024356001600160a01b038116810361023f579160443591606435916084359160a4359160c4359067ffffffffffffffff821161023f5761101691600401610f6d565b90565b61010090600319011261023f576004356001600160a01b038116810361023f57906024356001600160a01b038116810361023f579060443590606435906084359060a4359060c4359060e43590565b61012090600319011261023f576004356001600160a01b038116810361023f57906024356001600160a01b038116810361023f579060443590606435906084359060a4359060c43560ff8116810361023f579060e435906101043590565b467f000000000000000000000000000000000000000000000000000000000000000003611111577f000000000000000000000000000000000000000000000000000000000000000090565b61113a7f00000000000000000000000000000000000000000000000000000000000000006111b8565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a081526111b260c082610f1b565b51902090565b5f5b6020811080611263575b156111ed575f1981146111d9576001016111ba565b634e487b7160e01b5f52601160045260245ffd5b906111f782610f51565b916112056040519384610f1b565b808352601f1961121482610f51565b013660208501375f5b81811061122a5750505090565b602081101561124f57835181101561124f5780836001921a602082870101530161121d565b634e487b7160e01b5f52603260045260245ffd5b1561124f5781811a15156111c4565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925936112d18682896001600160a01b03165f5260036020526001600160a01b0360405f2091165f5260205260405f2055565b60405195865216941692a3565b91906112e98161203e565b6001600160a01b038316916113336001600160a01b0383169182857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020604051858152a361207b565b92805f52600660205260ff60405f20541691805f52600660205260ff60405f20541615158315151461141d575050156113da576001600160801b035f541680156113b2576113b0936113a56113a06113ab935f198164e8d4a510006001600160f01b038a16020101612639565b612657565b90612237565b6122f2565b565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091826113e6916120f1565b6001600160801b035f541680156113b2576113a06114179164e8d4a510006001600160f01b036113b0961602612639565b906121bb565b9392509493505f14611534576001600160801b035f541680156113b2576001600160701b0391611465916113a0916001600160f01b031664e8d4a510000281015f1901612639565b16905b835f5260066020526001600160f01b0360405f205460081c16936001600160f01b0383169485811061151a57506113b09394505f5260066020526114dc60405f206001600160f01b038481835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b5f5260066020526001600160f01b0360405f209181835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b859163db42144d60e01b5f5260045260245260445260645ffd5b90611468565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361156c57565b7f3f94bdf3000000000000000000000000000000000000000000000000000000005f5260045ffd5b61159d82612376565b6115a68161203e565b6115e56001600160a01b03821692835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020604051858152a361207b565b6001600160f01b0360045416926001600160f01b038216936001600160f01b0385820111908115611687575b5061165f575f52600660205260ff60405f2054165f1461165457506001600160801b035f541680156113b2576113a06114179164e8d4a510006113b09502612639565b906113b092506122f2565b7f51655895000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160701b038092506116ab6001600160f01b038683600554169401166120bd565b160110155f611611565b5f546001600160801b038082169216918083106117365750817fffffffffffffffffffffff00000000000000000000000000000000000000000074ffffffffff000000000000000000000000000000004260801b16921617175f557fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce26715f80a2565b827fecb4124f000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b61101691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742985521660408301526060820152606081526117b7608082610f1b565b5190206123aa565b9290916117f89260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c011690612566565b6006821015611820576113b0928261181957611814925061254b565b6123f8565b50506123f8565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b039061184783826124f2565b16805f52600260205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b93916117f89391612566565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166040517f8eaa6ac00000000000000000000000000000000000000000000000000000000081527f6561726e6572735f6c6973745f69676e6f7265640000000000000000000000006004820152602081602481855afa9081156119e9575f916119f4575b50159182159261193757505090565b602091925060446001600160a01b039160405194859384927fd7d1c1c00000000000000000000000000000000000000000000000000000000084527f6561726e6572730000000000000000000000000000000000000000000000000060048501521660248301525afa9081156119e9575f916119b1575090565b90506020813d6020116119e1575b816119cc60209383610f1b565b8101031261023f5751801515810361023f5790565b3d91506119bf565b6040513d5f823e3d90fd5b90506020813d602011611a1e575b81611a0f60209383610f1b565b8101031261023f57515f611928565b3d9150611a02565b6001600160a01b0316805f52600660205260405f20805460ff811615611b4557827f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a260ff19169055805f5260066020526001600160701b0360405f205460081c16908115611b41576001600160f01b0364e8d4a510006001600160801b035f541684020416905f526006602052611ae08160405f2090610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b036004549281841601169116176004556dffffffffffffffffffffffffffff196001600160701b03600554928184160316911617600555565b5050565b505050565b93909261101695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e081526117b761010082610f1b565b91611bc28183612606565b60068210156118205781611cb857611bdb91508461254b565b925b6006841015611820578315611cb2575f92611c21611c2f85946040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190610ecb565b03601f198101835282610f1b565b51915afa3d15611cab573d611c4381610f51565b90611c516040519283610f1b565b81523d5f602083013e5b81611c9d575b81611c78575b50611c75576113b0906123f8565b50565b805160208083019350908201919091031261023f5751630b135d3f60e11b145f611c67565b905060208151101590611c61565b6060611c5b565b50505050565b5092611bdd565b9594939291906001600160a01b03811696873303611ce2576113b0969750611f64565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b939293844211611daa57936001600160a01b0385611d3585856110169899611272565b1692835f52600160205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c081526117b760e082610f1b565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b6001600160a01b0316805f52600660205260405f2080549060ff8216611b455760016001600160f01b0392847f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a260ff19161780915560081c16908115611b41576001600160801b035f541680156113b2576113a0611e629164e8d4a510008502612639565b5f918252600660205260409091208054610100600160f81b0319166effffffffffffffffffffffffffff00600884901b161790556dffffffffffffffffffffffffffff196001600160701b036005549281841601169116176005557fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160316911617600455565b93909261101695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e081526117b761010082610f1b565b9193959490928042111561200f575085421015611fdf576113b0949550611f8b81836124f2565b6001600160a01b038216805f52600260205260405f20825f5260205260405f20600160ff198254161790557f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a36112de565b857fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b6001600160a01b031680156120505750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160f01b038111612095576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160801b035f54169081156113b257611016916113a0916001600160f01b031664e8d4a510000281015f1901612639565b6001600160a01b0316805f5260066020526001600160f01b0360405f205460081c166001600160f01b038316908181106121a25750505f52600660205261216860405f206001600160f01b038381835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160316911617600455565b8263db42144d60e01b5f5260045260245260445260645ffd5b6001600160a01b03165f52600660205261220e60405f206001600160f01b036001600160701b03841681835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6dffffffffffffffffffffffffffff196001600160701b03600554928184160116911617600555565b6001600160a01b0316805f5260066020526001600160f01b0360405f205460081c16906001600160701b038316918281106122d95750906122b0915f5260066020526001600160f01b0360405f209181835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b6dffffffffffffffffffffffffffff196001600160701b03600554928184160316911617600555565b9063db42144d60e01b5f5260045260245260445260645ffd5b6001600160a01b03165f52600660205261233c60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160116911617600455565b801561237f5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6123b26110c6565b906040519060208201927f1901000000000000000000000000000000000000000000000000000000000000845260228301526042820152604281526111b2606282610f1b565b9060068210156118205781156124ee5750600181146124df57600281146124b7576003811461248f57600481146124675760051461243f57638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600260205260405f20825f5260205260ff60405f20541661251d575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6001600160a01b03908116911603612561575f90565b600590565b907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116125fb5760ff1690601b821415806125f0575b6125e5576020935f93608093604051938452868401526040830152606082015282805260015afa156119e9575f516001600160a01b0316806125e157506001905f90565b5f91565b505050506004905f90565b50601c82141561259d565b505050506003905f90565b9060418151036126305761262c91602082015190606060408401519301515f1a90612566565b9091565b50506002905f90565b8115612643570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160701b038111612671576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220ec43bf74a336f78dc0774097414e0ac2c44e8090d020558a12e55b8384108c2c64736f6c634300081a0033000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde0314610e8f578063095ea7b314610e6957806318160ddd14610e0757806323b872dd14610d3057806326987b6014610a8d578063281b229d14610d0a5780632b20e39714610cc757806330adf81f14610c8d578063313ce56714610c505780633644e51514610c2e57806340c10f1914610c0a57806342966c6814610b76578063499d108114610b315780634c57a8fa14610b0b578063532992c514610ad857806353d96f2c14610ab2578063578f2aa014610a8d5780635a049a7014610a4a5780636425666b14610a0757806370a08231146109825780637ecebe001461094a5780637f2eecc31461091057806381399be4146108b957806384af270f1461087c57806384b0196e1461075b57806388b7ab63146107315780638a75f238146106d557806395d89b41146106815780639f8495f9146106685780639fd5a6cf1461060b578063a08cb48b146105e0578063a0cc6a68146105a6578063a36e40fc14610519578063a9059cbb146104e8578063ace150a5146104b7578063b7b7289914610465578063c634dfaa14610403578063cf092995146103d3578063d505accf14610380578063d916948714610346578063dd62ed3e146102f2578063e3ee160e146102c2578063e94a010214610279578063ef55bec6146102435763fc387d5a14610208575f80fd5b3461023f57602036600319011261023f576004356001600160801b038116810361023f5761023d9061023861153a565b6116b5565b005b5f80fd5b3461023f5761023d61027461025736611068565b9161026e8486888a8c989e988e9a9e9d9b9d611b4a565b8561188f565b611cbf565b3461023f57604036600319011261023f576001600160a01b0361029a610eef565b165f52600260205260405f206024355f52602052602060ff60405f2054166040519015158152f35b3461023f5761023d6102ed6102d636611068565b9161026e8486888a8c989e988e9a9e9d9b9d611ef7565b611f64565b3461023f57604036600319011261023f5761030b610eef565b6001600160a01b0361031b610f05565b91165f5260036020526001600160a01b0360405f2091165f52602052602060405f2054604051908152f35b3461023f575f36600319011261023f5760206040517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a15974298152f35b3461023f5760e036600319011261023f57610399610eef565b6103a1610f05565b906084359160ff8316830361023f5761023d9260c435926103cd60a43593606435906044359084611d12565b9061188f565b3461023f5761023d6102ed6103e736610fb3565b6103fd828486888a8c989c9d979d9b999b611ef7565b83611bb7565b3461023f57602036600319011261023f576001600160a01b03610424610eef565b165f52600660205260405f205460ff81165f1461045c576001600160701b0360209160081c165b6001600160701b0360405191168152f35b5060205f61044b565b3461023f57606036600319011261023f5761047e610eef565b6024356044359167ffffffffffffffff831161023f576104b26104a861023d943690600401610f6d565b6103fd8484611765565b611834565b3461023f5761023d6102ed6104cb36611019565b906104e2838587898b8d999d9e989e9c9a9c611ef7565b846117bf565b3461023f57604036600319011261023f5761050e610504610eef565b60243590336112de565b602060405160018152f35b3461023f575f36600319011261023f576105323361189b565b1561057e5764e8d4a510006001600160801b035f5416146105565761023d33611dda565b7f5dcd0a37000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdd93dca8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461023f575f36600319011261023f5760206040517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678152f35b3461023f5761023d6102746105f436611019565b906104e2838587898b8d999d9e989e9c9a9c611b4a565b3461023f5760a036600319011261023f57610624610eef565b61062c610f05565b6084359167ffffffffffffffff831161023f5761066261065361023d943690600401610f6d565b92606435906044359084611d12565b90611bb7565b3461023f575f36600319011261023f5761023d33611a26565b3461023f575f36600319011261023f576106d16106bd7f4d000000000000000000000000000000000000000000000000000000000000006111b8565b604051918291602083526020830190610ecb565b0390f35b3461023f575f36600319011261023f5760206107206001600160701b036005541664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b6001600160f01b0360405191168152f35b3461023f5761023d61027461074536610fb3565b6103fd828486888a8c989c9d979d9b999b611b4a565b3461023f575f36600319011261023f576108206107977f4d206279204d5e300000000000000000000000000000000000000000000000006111b8565b60405160206107a68183610f1b565b5f82525f36813761082e6040516107be604082610f1b565b600181527f3100000000000000000000000000000000000000000000000000000000000000838201526040519586957f0f00000000000000000000000000000000000000000000000000000000000000875260e08588015260e0870190610ecb565b908582036040870152610ecb565b4660608501523060808501525f60a085015283810360c08501528180845192838152019301915f5b82811061086557505050500390f35b835185528695509381019392810192600101610856565b3461023f57602036600319011261023f576001600160a01b0361089d610eef565b165f526006602052602060ff60405f2054166040519015158152f35b3461023f57602036600319011261023f576108d2610eef565b6108db8161189b565b6108e85761023d90611a26565b7f8b198077000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461023f575f36600319011261023f5760206040517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88152f35b3461023f57602036600319011261023f576001600160a01b0361096b610eef565b165f526001602052602060405f2054604051908152f35b3461023f57602036600319011261023f576001600160a01b036109a3610eef565b165f526006602052602060405f205460ff81165f146109f6576001600160701b036107209160081c1664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b60081c6001600160f01b0316610720565b3461023f575f36600319011261023f5760206040516001600160a01b037f000000000000000000000000d925c84b55e4e44a53749ff5f2a5a13f63d128fd168152f35b3461023f5760a036600319011261023f57610a63610eef565b6024356044359160ff8316830361023f576104b261023d93608435906064359061026e8686611765565b3461023f575f36600319011261023f5760206001600160801b035f5416604051908152f35b3461023f575f36600319011261023f57602064ffffffffff5f5460801c16604051908152f35b3461023f57608036600319011261023f5761023d610af4610eef565b602435906104b26064356044356104e28585611765565b3461023f575f36600319011261023f5760206001600160701b0360055416604051908152f35b3461023f57606036600319011261023f57610b4a610eef565b604435906001600160801b038216820361023f57610b6d61023d9261023861153a565b60243590611594565b3461023f57602036600319011261023f57600435610b9261153a565b610b9b81612376565b5f6040518281527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203392a3335f52600660205260ff60405f2054165f14610bf857610bf2610bed61023d9261207b565b6120bd565b33612237565b610c0461023d9161207b565b336120f1565b3461023f57604036600319011261023f5761023d610c26610eef565b610b6d61153a565b3461023f575f36600319011261023f576020610c486110c6565b604051908152f35b3461023f575f36600319011261023f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b3461023f575f36600319011261023f5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b3461023f575f36600319011261023f5760206040516001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c168152f35b3461023f575f36600319011261023f5760206001600160f01b0360045416604051908152f35b3461023f57606036600319011261023f57610d49610eef565b610d51610f05565b604435906001600160a01b0383165f52600360205260405f206001600160a01b0333165f5260205260405f2054925f198403610d92575b61050e93506112de565b828410610dd357610dce8361050e950333836001600160a01b03165f5260036020526001600160a01b0360405f2091165f5260205260405f2055565b610d88565b82847f192b9e4e000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461023f575f36600319011261023f5760206001600160f01b038060045416610e5f6001600160701b036005541664e8d4a510006001600160f01b03916001600160701b036001600160801b035f5416911602041690565b0116604051908152f35b3461023f57604036600319011261023f5761050e610e85610eef565b6024359033611272565b3461023f575f36600319011261023f576106d16106bd7f4d206279204d5e300000000000000000000000000000000000000000000000006111b8565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361023f57565b602435906001600160a01b038216820361023f57565b90601f8019910116810190811067ffffffffffffffff821117610f3d57604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610f3d57601f01601f191660200190565b81601f8201121561023f57803590610f8482610f51565b92610f926040519485610f1b565b8284526020838301011161023f57815f926020809301838601378301015290565b60e060031982011261023f576004356001600160a01b038116810361023f57916024356001600160a01b038116810361023f579160443591606435916084359160a4359160c4359067ffffffffffffffff821161023f5761101691600401610f6d565b90565b61010090600319011261023f576004356001600160a01b038116810361023f57906024356001600160a01b038116810361023f579060443590606435906084359060a4359060c4359060e43590565b61012090600319011261023f576004356001600160a01b038116810361023f57906024356001600160a01b038116810361023f579060443590606435906084359060a4359060c43560ff8116810361023f579060e435906101043590565b467f000000000000000000000000000000000000000000000000000000000000000a03611111577f6640ff0f84e8bab057c1948202cdcb20e2b7638c1e9824626c02ae5e542cd1fb90565b61113a7f4d206279204d5e300000000000000000000000000000000000000000000000006111b8565b6020815191012060405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a081526111b260c082610f1b565b51902090565b5f5b6020811080611263575b156111ed575f1981146111d9576001016111ba565b634e487b7160e01b5f52601160045260245ffd5b906111f782610f51565b916112056040519384610f1b565b808352601f1961121482610f51565b013660208501375f5b81811061122a5750505090565b602081101561124f57835181101561124f5780836001921a602082870101530161121d565b634e487b7160e01b5f52603260045260245ffd5b1561124f5781811a15156111c4565b919060206001600160a01b03807f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925936112d18682896001600160a01b03165f5260036020526001600160a01b0360405f2091165f5260205260405f2055565b60405195865216941692a3565b91906112e98161203e565b6001600160a01b038316916113336001600160a01b0383169182857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020604051858152a361207b565b92805f52600660205260ff60405f20541691805f52600660205260ff60405f20541615158315151461141d575050156113da576001600160801b035f541680156113b2576113b0936113a56113a06113ab935f198164e8d4a510006001600160f01b038a16020101612639565b612657565b90612237565b6122f2565b565b7f23d359a3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091826113e6916120f1565b6001600160801b035f541680156113b2576113a06114179164e8d4a510006001600160f01b036113b0961602612639565b906121bb565b9392509493505f14611534576001600160801b035f541680156113b2576001600160701b0391611465916113a0916001600160f01b031664e8d4a510000281015f1901612639565b16905b835f5260066020526001600160f01b0360405f205460081c16936001600160f01b0383169485811061151a57506113b09394505f5260066020526114dc60405f206001600160f01b038481835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b5f5260066020526001600160f01b0360405f209181835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b859163db42144d60e01b5f5260045260245260445260645ffd5b90611468565b6001600160a01b037f000000000000000000000000d925c84b55e4e44a53749ff5f2a5a13f63d128fd16330361156c57565b7f3f94bdf3000000000000000000000000000000000000000000000000000000005f5260045ffd5b61159d82612376565b6115a68161203e565b6115e56001600160a01b03821692835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020604051858152a361207b565b6001600160f01b0360045416926001600160f01b038216936001600160f01b0385820111908115611687575b5061165f575f52600660205260ff60405f2054165f1461165457506001600160801b035f541680156113b2576113a06114179164e8d4a510006113b09502612639565b906113b092506122f2565b7f51655895000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160701b038092506116ab6001600160f01b038683600554169401166120bd565b160110155f611611565b5f546001600160801b038082169216918083106117365750817fffffffffffffffffffffff00000000000000000000000000000000000000000074ffffffffff000000000000000000000000000000004260801b16921617175f557fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce26715f80a2565b827fecb4124f000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b61101691604051906001600160a01b0360208301937f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742985521660408301526060820152606081526117b7608082610f1b565b5190206123aa565b9290916117f89260ff601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851694821c011690612566565b6006821015611820576113b0928261181957611814925061254b565b6123f8565b50506123f8565b634e487b7160e01b5f52602160045260245ffd5b6001600160a01b039061184783826124f2565b16805f52600260205260405f20825f5260205260405f20600160ff198254161790557f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d815f80a3565b93916117f89391612566565b6001600160a01b037f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c166040517f8eaa6ac00000000000000000000000000000000000000000000000000000000081527f6561726e6572735f6c6973745f69676e6f7265640000000000000000000000006004820152602081602481855afa9081156119e9575f916119f4575b50159182159261193757505090565b602091925060446001600160a01b039160405194859384927fd7d1c1c00000000000000000000000000000000000000000000000000000000084527f6561726e6572730000000000000000000000000000000000000000000000000060048501521660248301525afa9081156119e9575f916119b1575090565b90506020813d6020116119e1575b816119cc60209383610f1b565b8101031261023f5751801515810361023f5790565b3d91506119bf565b6040513d5f823e3d90fd5b90506020813d602011611a1e575b81611a0f60209383610f1b565b8101031261023f57515f611928565b3d9150611a02565b6001600160a01b0316805f52600660205260405f20805460ff811615611b4557827f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d5f80a260ff19169055805f5260066020526001600160701b0360405f205460081c16908115611b41576001600160f01b0364e8d4a510006001600160801b035f541684020416905f526006602052611ae08160405f2090610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b036004549281841601169116176004556dffffffffffffffffffffffffffff196001600160701b03600554928184160316911617600555565b5050565b505050565b93909261101695926001600160a01b03604051958160208801987fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de88a52166040880152166060860152608085015260a084015260c083015260e082015260e081526117b761010082610f1b565b91611bc28183612606565b60068210156118205781611cb857611bdb91508461254b565b925b6006841015611820578315611cb2575f92611c21611c2f85946040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190610ecb565b03601f198101835282610f1b565b51915afa3d15611cab573d611c4381610f51565b90611c516040519283610f1b565b81523d5f602083013e5b81611c9d575b81611c78575b50611c75576113b0906123f8565b50565b805160208083019350908201919091031261023f5751630b135d3f60e11b145f611c67565b905060208151101590611c61565b6060611c5b565b50505050565b5092611bdd565b9594939291906001600160a01b03811696873303611ce2576113b0969750611f64565b877f1c5939f3000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b939293844211611daa57936001600160a01b0385611d3585856110169899611272565b1692835f52600160205260405f20908154916001830190556001600160a01b036040519460208601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988526040870152166060850152608084015260a083015260c082015260c081526117b760e082610f1b565b847ff88f0490000000000000000000000000000000000000000000000000000000005f526004524260245260445ffd5b6001600160a01b0316805f52600660205260405f2080549060ff8216611b455760016001600160f01b0392847f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c5f80a260ff19161780915560081c16908115611b41576001600160801b035f541680156113b2576113a0611e629164e8d4a510008502612639565b5f918252600660205260409091208054610100600160f81b0319166effffffffffffffffffffffffffff00600884901b161790556dffffffffffffffffffffffffffff196001600160701b036005549281841601169116176005557fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160316911617600455565b93909261101695926001600160a01b03604051958160208801987f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a22678a52166040880152166060860152608085015260a084015260c083015260e082015260e081526117b761010082610f1b565b9193959490928042111561200f575085421015611fdf576113b0949550611f8b81836124f2565b6001600160a01b038216805f52600260205260405f20825f5260205260405f20600160ff198254161790557f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a55f80a36112de565b857fb3fcd33e000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f498ff9a2000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b6001600160a01b031680156120505750565b7f17858bbe000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160f01b038111612095576001600160f01b031690565b7f2a49c10d000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160801b035f54169081156113b257611016916113a0916001600160f01b031664e8d4a510000281015f1901612639565b6001600160a01b0316805f5260066020526001600160f01b0360405f205460081c166001600160f01b038316908181106121a25750505f52600660205261216860405f206001600160f01b038381835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160316911617600455565b8263db42144d60e01b5f5260045260245260445260645ffd5b6001600160a01b03165f52600660205261220e60405f206001600160f01b036001600160701b03841681835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b6dffffffffffffffffffffffffffff196001600160701b03600554928184160116911617600555565b6001600160a01b0316805f5260066020526001600160f01b0360405f205460081c16906001600160701b038316918281106122d95750906122b0915f5260066020526001600160f01b0360405f209181835460081c160316610100600160f81b0319610100600160f81b0383549260081b169116179055565b6dffffffffffffffffffffffffffff196001600160701b03600554928184160316911617600555565b9063db42144d60e01b5f5260045260245260445260645ffd5b6001600160a01b03165f52600660205261233c60405f206001600160f01b038381835460081c160116610100600160f81b0319610100600160f81b0383549260081b169116179055565b7fffff0000000000000000000000000000000000000000000000000000000000006001600160f01b03600454928184160116911617600455565b801561237f5750565b7f77b8dde3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6123b26110c6565b906040519060208201927f1901000000000000000000000000000000000000000000000000000000000000845260228301526042820152604281526111b2606282610f1b565b9060068210156118205781156124ee5750600181146124df57600281146124b7576003811461248f57600481146124675760051461243f57638baa579f60e01b5f5260045ffd5b7f10c74b03000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff551e89000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbf4bf5b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4be6321b000000000000000000000000000000000000000000000000000000005f5260045ffd5b638baa579f60e01b5f5260045ffd5b9050565b6001600160a01b0316805f52600260205260405f20825f5260205260ff60405f20541661251d575050565b7fd309466d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6001600160a01b03908116911603612561575f90565b600590565b907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116125fb5760ff1690601b821415806125f0575b6125e5576020935f93608093604051938452868401526040830152606082015282805260015afa156119e9575f516001600160a01b0316806125e157506001905f90565b5f91565b505050506004905f90565b50601c82141561259d565b505050506003905f90565b9060418151036126305761262c91602082015190606060408401519301515f1a90612566565b9091565b50506002905f90565b8115612643570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160701b038111612671576001600160701b031690565b7fca21dbd1000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220ec43bf74a336f78dc0774097414e0ac2c44e8090d020558a12e55b8384108c2c64736f6c634300081a0033

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

000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c

-----Decoded View---------------
Arg [0] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.