ETH Price: $3,339.01 (-3.41%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer215734372025-01-07 15:17:5924 hrs ago1736263079IN
M^0: M Token
0 ETH0.0025259630.9565514
Transfer215249462024-12-31 20:50:237 days ago1735678223IN
M^0: M Token
0 ETH0.000238096.78850626
Transfer215249442024-12-31 20:49:597 days ago1735678199IN
M^0: M Token
0 ETH0.000241346.88118661
Transfer215163362024-12-30 15:58:239 days ago1735574303IN
M^0: M Token
0 ETH0.00080689.8878006
Transfer215163062024-12-30 15:52:239 days ago1735573943IN
M^0: M Token
0 ETH0.0009281211.37446725
Approve215160982024-12-30 15:10:239 days ago1735571423IN
M^0: M Token
0 ETH0.0003371612.81516242
Approve215160912024-12-30 15:08:599 days ago1735571339IN
M^0: M Token
0 ETH0.0006385113.8175897
Transfer215160772024-12-30 15:06:119 days ago1735571171IN
M^0: M Token
0 ETH0.0004908313.98974056
Transfer215160292024-12-30 14:56:359 days ago1735570595IN
M^0: M Token
0 ETH0.0006839213.11485735
Transfer215160192024-12-30 14:54:359 days ago1735570475IN
M^0: M Token
0 ETH0.0010227812.53659162
Transfer215159862024-12-30 14:47:599 days ago1735570079IN
M^0: M Token
0 ETH0.0012304115.07912655
Transfer215140332024-12-30 8:15:239 days ago1735546523IN
M^0: M Token
0 ETH0.000310283.80438567
Approve215011062024-12-28 12:57:2311 days ago1735390643IN
M^0: M Token
0 ETH0.000195624.23331519
Transfer215010862024-12-28 12:53:2311 days ago1735390403IN
M^0: M Token
0 ETH0.000205413.93710785
Transfer215010742024-12-28 12:50:4711 days ago1735390247IN
M^0: M Token
0 ETH0.000343254.20734649
Approve215010272024-12-28 12:41:2311 days ago1735389683IN
M^0: M Token
0 ETH0.00019224.15946752
Transfer215010162024-12-28 12:39:1111 days ago1735389551IN
M^0: M Token
0 ETH0.00020843.99445886
Transfer215010052024-12-28 12:36:4711 days ago1735389407IN
M^0: M Token
0 ETH0.000319653.91811881
Transfer215008552024-12-28 12:06:4711 days ago1735387607IN
M^0: M Token
0 ETH0.000379334.6482314
Transfer215008302024-12-28 12:01:4711 days ago1735387307IN
M^0: M Token
0 ETH0.000503646.17414729
Transfer214955892024-12-27 18:28:3511 days ago1735324115IN
M^0: M Token
0 ETH0.000543177.07286815
Approve214955432024-12-27 18:19:1111 days ago1735323551IN
M^0: M Token
0 ETH0.000325977.05242703
Transfer214955342024-12-27 18:17:2311 days ago1735323443IN
M^0: M Token
0 ETH0.000331986.36169425
Transfer214955112024-12-27 18:12:4711 days ago1735323167IN
M^0: M Token
0 ETH0.000645397.91082489
Approve214954552024-12-27 18:01:3511 days ago1735322495IN
M^0: M Token
0 ETH0.000430199.30709146
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MToken

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 999999 runs

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

pragma solidity 0.8.23;

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 { TTGRegistrarReader } from "./libs/TTGRegistrarReader.sol";

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

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

/*

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

-->> M is for Money. <<--

*/

/**
 * @title  MToken
 * @author M^0 Labs
 * @notice ERC20 M Token.
 */
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 minterGateway;

    /// @inheritdoc IMToken
    address public immutable ttgRegistrar;

    /// @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 Minter Gateway.
    modifier onlyMinterGateway() {
        if (msg.sender != minterGateway) revert NotMinterGateway();

        _;
    }

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

    /**
     * @notice Constructs the M Token contract.
     * @param  ttgRegistrar_ The address of the TTG Registrar contract.
     * @param  minterGateway_     The address of Minter Gateway.
     */
    constructor(address ttgRegistrar_, address minterGateway_) ContinuousIndexing() ERC20Extended("M by M^0", "M", 6) {
        if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar();
        if ((minterGateway = minterGateway_) == address(0)) revert ZeroMinterGateway();
    }

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

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

    /// @inheritdoc IMToken
    function burn(address account_, uint256 amount_) external onlyMinterGateway {
        _burn(account_, amount_);
    }

    /// @inheritdoc IMToken
    function startEarning() external {
        if (!_isApprovedEarner(msg.sender)) revert NotApprovedEarner();

        _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 rateModel() public view returns (address rateModel_) {
        return TTGRegistrarReader.getEarnerRateModel(ttgRegistrar);
    }

    /// @inheritdoc IMToken
    function earnerRate() public view returns (uint32 earnerRate_) {
        return _latestRate;
    }

    /// @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) {
        // NOTE: Safe to use unchecked here, since `block.timestamp` is always greater than `latestUpdateTimestamp`.
        unchecked {
            return
                // NOTE: Cap the index to `type(uint128).max` to prevent overflow in present value math.
                UIntMath.bound128(
                    ContinuousIndexingMath.multiplyIndicesDown(
                        latestIndex,
                        ContinuousIndexingMath.getContinuousIndex(
                            ContinuousIndexingMath.convertFromBasisPoints(_latestRate),
                            uint32(block.timestamp - latestUpdateTimestamp)
                        )
                    )
                );
        }
    }

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

        updateIndex();
    }

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

        updateIndex();
    }

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

        updateIndex();
    }

    /**
     * @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 TTG.
     * @param  account_    The account to check.
     * @return True if approved, false otherwise.
     */
    function _isApprovedEarner(address account_) internal view returns (bool) {
        return
            TTGRegistrarReader.isEarnersListIgnored(ttgRegistrar) ||
            TTGRegistrarReader.isApprovedEarner(ttgRegistrar, account_);
    }

    /**
     * @dev    Gets the current earner rate from TTG approved rate model contract.
     * @return rate_ The current earner rate.
     */
    function _rate() internal view override returns (uint32 rate_) {
        (bool success_, bytes memory returnData_) = rateModel().staticcall(
            abi.encodeWithSelector(IRateModel.rate.selector)
        );

        rate_ = (success_ && returnData_.length >= 32) ? UIntMath.bound32(abi.decode(returnData_, (uint256))) : 0;
    }

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

File 2 of 21 : ERC20Extended.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

    /// @inheritdoc IERC20
    string public 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 = 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 name_) {
        return _name;
    }

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

File 3 of 21 : UIntMath.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 4 of 21 : IERC20.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 5 of 21 : TTGRegistrarReader.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  Library to read TTG (Two Token Governance) Registrar contract parameters.
 * @author M^0 Labs
 */
library TTGRegistrarReader {
    /* ============ Variables ============ */

    /// @notice The name of parameter in TTG that defines the earner rate model contract.
    bytes32 internal constant EARNER_RATE_MODEL = "earner_rate_model";

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

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

    /// @notice The parameter name in TTG that defines the time to wait for mint request to be processed.
    bytes32 internal constant MINT_DELAY = "mint_delay";

    /// @notice The parameter name in TTG that defines the mint ratio.
    bytes32 internal constant MINT_RATIO = "mint_ratio"; // bps

    /// @notice The parameter name in TTG that defines the time while mint request can still be processed.
    bytes32 internal constant MINT_TTL = "mint_ttl";

    /// @notice The parameter name in TTG that defines the time to freeze minter.
    bytes32 internal constant MINTER_FREEZE_TIME = "minter_freeze_time";

    /// @notice The parameter name in TTG that defines the minter rate model contract.
    bytes32 internal constant MINTER_RATE_MODEL = "minter_rate_model";

    /// @notice The parameter name in TTG that defines the minters list.
    bytes32 internal constant MINTERS_LIST = "minters";

    /// @notice The parameter name in TTG that defines the penalty rate.
    bytes32 internal constant PENALTY_RATE = "penalty_rate"; // bps

    /// @notice The parameter name in TTG that defines the required interval to update collateral.
    bytes32 internal constant UPDATE_COLLATERAL_INTERVAL = "update_collateral_interval";

    /// @notice The parameter name that defines number of signatures required for successful collateral update.
    bytes32 internal constant UPDATE_COLLATERAL_VALIDATOR_THRESHOLD = "update_collateral_threshold";

    /// @notice The parameter name in TTG that defines the validators list.
    bytes32 internal constant VALIDATORS_LIST = "validators";

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

    /// @notice Gets the earner rate model contract address.
    function getEarnerRateModel(address registrar_) internal view returns (address) {
        return toAddress(_get(registrar_, EARNER_RATE_MODEL));
    }

    /// @notice Gets the mint delay.
    function getMintDelay(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, MINT_DELAY));
    }

    /// @notice Gets the minter freeze time.
    function getMinterFreezeTime(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, MINTER_FREEZE_TIME));
    }

    /// @notice Gets the minter rate model contract address.
    function getMinterRateModel(address registrar_) internal view returns (address) {
        return toAddress(_get(registrar_, MINTER_RATE_MODEL));
    }

    /// @notice Gets the mint TTL.
    function getMintTTL(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, MINT_TTL));
    }

    /// @notice Gets the mint ratio.
    function getMintRatio(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, MINT_RATIO));
    }

    /// @notice Gets the update collateral interval.
    function getUpdateCollateralInterval(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, UPDATE_COLLATERAL_INTERVAL));
    }

    /// @notice Gets the update collateral validator threshold.
    function getUpdateCollateralValidatorThreshold(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, UPDATE_COLLATERAL_VALIDATOR_THRESHOLD));
    }

    /// @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 Checks if the given minter is approved.
    function isApprovedMinter(address registrar_, address minter_) internal view returns (bool) {
        return _contains(registrar_, MINTERS_LIST, minter_);
    }

    /// @notice Checks if the given validator is approved.
    function isApprovedValidator(address registrar_, address validator_) internal view returns (bool) {
        return _contains(registrar_, VALIDATORS_LIST, validator_);
    }

    /// @notice Gets the penalty rate.
    function getPenaltyRate(address registrar_) internal view returns (uint256) {
        return uint256(_get(registrar_, PENALTY_RATE));
    }

    /// @notice Gets the vault contract address.
    function getVault(address registrar_) internal view returns (address) {
        return ITTGRegistrar(registrar_).vault();
    }

    /// @notice Converts given bytes32 to address.
    function toAddress(bytes32 input_) internal pure returns (address) {
        return address(uint160(uint256(input_)));
    }

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

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

File 6 of 21 : IContinuousIndexing.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

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

    /**
     * @notice Updates the latest index and latest accrual time in storage.
     * @return index The new stored index for computing present amounts from principal amounts.
     */
    function updateIndex() external returns (uint128);

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

    /// @notice The current index that would be written to storage if `updateIndex` is 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);
}

File 7 of 21 : IMToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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 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 TTG.
    error IsApprovedEarner();

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

    /// @notice Emitted when calling `mint`, `burn` not by Minter Gateway.
    error NotMinterGateway();

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

    /// @notice Emitted in constructor if Minter Gateway is 0x0.
    error ZeroMinterGateway();

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

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

    /**
     * @notice Mints tokens.
     * @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 tokens.
     * @param  account The address of account to burn from.
     * @param  amount  The amount of M Token to burn.
     */
    function burn(address account, uint256 amount) 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 TTG Registrar.
     * @param  account The account to stop earning for.
     */
    function stopEarning(address account) external;

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

    /// @notice The address of the Minter Gateway contract.
    function minterGateway() external view returns (address);

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

    /// @notice The address of TTG approved earner rate model.
    function rateModel() external view returns (address);

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

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

File 8 of 21 : IRateModel.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  Rate Model Interface.
 * @author M^0 Labs
 */
interface IRateModel {
    /**
     * @notice Returns the current yearly rate in BPS.
     *         This value does not account for the compounding interest.
     */
    function rate() external view returns (uint256);
}

File 9 of 21 : ContinuousIndexing.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

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

    /// @inheritdoc IContinuousIndexing
    uint128 public latestIndex;

    /// @dev The latest updated rate.
    uint32 internal _latestRate;

    /// @inheritdoc IContinuousIndexing
    uint40 public latestUpdateTimestamp;

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

    /// @notice Constructs the ContinuousIndexing contract.
    constructor() {
        latestIndex = ContinuousIndexingMath.EXP_SCALED_ONE;
        latestUpdateTimestamp = uint40(block.timestamp);
    }

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

    /// @inheritdoc IContinuousIndexing
    function updateIndex() public virtual returns (uint128 currentIndex_) {
        // NOTE: `_rate()` can depend indirectly on `latestIndex` and `latestUpdateTimestamp`, if the RateModel
        //       depends on earning balances/supply, which depends on `currentIndex()`, so only update them after this.
        uint32 rate_ = _rate();

        if (latestUpdateTimestamp == block.timestamp && _latestRate == rate_) return latestIndex;

        // NOTE: `currentIndex()` depends on `_latestRate`, so only update it after this.
        latestIndex = currentIndex_ = currentIndex();
        _latestRate = rate_;
        latestUpdateTimestamp = uint40(block.timestamp);

        emit IndexUpdated(currentIndex_, rate_);
    }

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

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

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

    /// @dev To be overridden by the inheriting contract to return the current rate.
    function _rate() internal view virtual returns (uint32);
}

File 10 of 21 : ContinuousIndexingMath.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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 number of seconds in a year.
    uint32 internal constant SECONDS_PER_YEAR = 31_536_000;

    /// @notice 100% in basis points.
    uint16 internal constant BPS_SCALED_ONE = 1e4;

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

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

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

    /**
     * @notice Helper function to calculate e^rt (continuous compounding formula).
     * @dev    `uint64 yearlyRate` can accommodate 1000% interest per year.
     * @dev    `uint32 time` can accommodate 100 years.
     * @dev    `type(uint64).max * type(uint32).max / SECONDS_PER_YEAR` fits in a `uint72`.
     */
    function getContinuousIndex(uint64 yearlyRate, uint32 time) internal pure returns (uint48 index) {
        unchecked {
            // NOTE: Casting `uint256(yearlyRate) * time` to a `uint72` is safe because the largest value is
            //      `type(uint64).max * type(uint32).max / SECONDS_PER_YEAR`, which is less than `type(uint72).max`.
            return exponent(uint72((uint256(yearlyRate) * time) / SECONDS_PER_YEAR));
        }
    }

    /**
     * @notice Helper function to calculate y = e^x using R(4,4) Padé approximation:
     *           e(x) = (1 + x/2 + 3(x^2)/28 + x^3/84 + x^4/1680) / (1 - x/2 + 3(x^2)/28 - x^3/84 + x^4/1680)
     *           See: https://en.wikipedia.org/wiki/Pad%C3%A9_table
     *           See: https://www.wolframalpha.com/input?i=PadeApproximant%5Bexp%5Bx%5D%2C%7Bx%2C0%2C%7B4%2C+4%7D%7D%5D
     *         Despite itself being a whole number, `x` represents a real number scaled by `EXP_SCALED_ONE`, thus
     *         allowing for y = e^x where x is a real number.
     * @dev    Output `y` for a `uint72` input `x` will fit in `uint48`
     */
    function exponent(uint72 x) internal pure returns (uint48 y) {
        // NOTE: This can be done unchecked even for `x = type(uint72).max`.
        //       Verify by removing `unchecked` and running `test_exponent()`.
        unchecked {
            uint256 x2 = uint256(x) * x;

            // `additiveTerms` is `(1 + 3(x^2)/28 + x^4/1680)`, and scaled by `84e27`.
            // NOTE: `84e27` the cleanest and largest scalar, given the various intermediate overflow possibilities.
            // NOTE: The resulting `(x2 * x2) / 20e21` term has been split up in order to avoid overflow of `x2 * x2`.
            uint256 additiveTerms = 84e27 + (9e3 * x2) + ((x2 / 2e11) * (x2 / 1e11));

            // `differentTerms` is `(- x/2 - x^3/84)`, but positive (will be subtracted later), and scaled by `84e27`.
            uint256 differentTerms = uint256(x) * (42e15 + (x2 / 1e9));

            // Result needs to be scaled by `1e12`.
            // NOTE: Can cast to `uint48` because contents can never be larger than `type(uint48).max` for any `x`.
            //       Max `y` is ~200e12, before falling off. See links above for reference.
            return uint48(((additiveTerms + differentTerms) * 1e12) / (additiveTerms - differentTerms));
        }
    }

    /**
     * @notice Helper function to convert 12-decimal representation to basis points.
     * @param  input The input in 12-decimal representation.
     * @return The output in basis points.
     */
    function convertToBasisPoints(uint64 input) internal pure returns (uint40) {
        unchecked {
            return uint40((uint256(input) * BPS_SCALED_ONE) / EXP_SCALED_ONE);
        }
    }

    /**
     * @notice Helper function to convert basis points to 12-decimal representation.
     * @param  input The input in basis points.
     * @return The output in 12-decimal representation.
     */
    function convertFromBasisPoints(uint32 input) internal pure returns (uint64) {
        unchecked {
            return uint64((uint256(input) * EXP_SCALED_ONE) / BPS_SCALED_ONE);
        }
    }
}

File 11 of 21 : IERC20Extended.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 12 of 21 : ERC3009.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 13 of 21 : ITTGRegistrar.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  TTG (Two Token Governance) Registrar interface.
 * @author M^0 Labs
 */
interface ITTGRegistrar {
    /**
     * @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 True if the list contains the account, false otherwise.
     */
    function listContains(bytes32 list, address account) external view returns (bool);

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

File 14 of 21 : IERC3009.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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_) {}
}

File 16 of 21 : IStatefulERC712.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 17 of 21 : ERC712Extended.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

    /**
     * @notice Constructs the EIP-712 domain separator.
     * @param  name_ The name of the contract.
     */
    constructor(string memory name_) {
        _name = 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
            _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(_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();
    }
}

File 18 of 21 : IERC712Extended.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 19 of 21 : IERC712.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

File 20 of 21 : SignatureChecker.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

pragma solidity 0.8.23;

/**
 * @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/protocol/lib/common/src/",
    "contract-test-utils/=lib/ttg/lib/erc20-helper/lib/contract-test-utils/contracts/",
    "ds-test/=lib/protocol/lib/solmate/lib/ds-test/src/",
    "erc20-helper/=lib/ttg/lib/erc20-helper/src/",
    "forge-std/=lib/forge-std/src/",
    "protocol/=lib/protocol/",
    "solmate/=lib/protocol/lib/solmate/src/",
    "ttg/=lib/ttg/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"ttgRegistrar_","type":"address"},{"internalType":"address","name":"minterGateway_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationAlreadyUsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"}],"name":"AuthorizationExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"}],"name":"AuthorizationNotYetValid","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"payee","type":"address"}],"name":"CallerMustBePayee","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[{"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":"NotMinterGateway","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":"ZeroMinterGateway","type":"error"},{"inputs":[],"name":"ZeroTTGRegistrar","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"},{"indexed":true,"internalType":"uint32","name":"rate","type":"uint32"}],"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":"address","name":"account_","type":"address"},{"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":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"earnerRate","outputs":[{"internalType":"uint32","name":"earnerRate_","type":"uint32"}],"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":[],"name":"minterGateway","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"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":[{"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":[],"name":"rateModel","outputs":[{"internalType":"address","name":"rateModel_","type":"address"}],"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":"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":[],"name":"ttgRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateIndex","outputs":[{"internalType":"uint128","name":"currentIndex_","type":"uint128"}],"stateMutability":"nonpayable","type":"function"}]

61012060405234801562000011575f80fd5b50604051620040ee380380620040ee833981016040819052620000349162000210565b604080518082018252600881526704d206279204d5e360c41b60208083019190915282518084019093526001808452604d60f81b918401919091525f805464ffffffffff4216600160a01b027fffffffffffffff0000000000ffffffff000000000000000000000000000000009091161764e8d4a51000179055909190600690839081908190620000c68282620002e4565b5046608052620000d562000157565b60a0525060049150620000eb90508382620002e4565b5060ff1660c05250506001600160a01b038216610100819052620001225760405163b8eb034b60e01b815260040160405180910390fd5b6001600160a01b03811660e08190526200014f57604051630205ebf760e31b815260040160405180910390fd5b50506200042a565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b60016040516200018c9190620003b0565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80516001600160a01b03811681146200020b575f80fd5b919050565b5f806040838503121562000222575f80fd5b6200022d83620001f4565b91506200023d60208401620001f4565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200026f57607f821691505b6020821081036200028e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620002df57805f5260205f20601f840160051c81016020851015620002bb5750805b601f840160051c820191505b81811015620002dc575f8155600101620002c7565b50505b505050565b81516001600160401b0381111562000300576200030062000246565b62000318816200031184546200025a565b8462000294565b602080601f8311600181146200034e575f8415620003365750858301515b5f19600386901b1c1916600185901b178555620003a8565b5f85815260208120601f198616915b828110156200037e578886015182559484019460019091019084016200035d565b50858210156200039c57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f808354620003bf816200025a565b60018281168015620003da5760018114620003f0576200041e565b60ff19841687528215158302870194506200041e565b875f526020805f205f5b85811015620004155781548a820152908401908201620003fa565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161010051613c636200048b5f395f81816106c901528181610f8a01528181611a090152611a3801525f818161046b01528181610b070152610ede01525f61041501525f610acd01525f610a9d0152613c635ff3fe608060405234801561000f575f80fd5b50600436106102f9575f3560e01c80638a75f23811610192578063ace150a5116100e8578063d505accf11610093578063e3ee160e1161006e578063e3ee160e146107e7578063e94a0102146107fa578063ef55bec614610827575f80fd5b8063d505accf14610783578063d916948714610796578063dd62ed3e146107bd575f80fd5b8063c23465b3116100c3578063c23465b31461072c578063c634dfaa1461075d578063cf09299514610770575f80fd5b8063ace150a5146106fe578063b7b7289914610711578063b9f412b014610724575f80fd5b8063a08cb48b11610148578063a36e40fc11610123578063a36e40fc146106bc578063a6ce63cd146106c4578063a9059cbb146106eb575f80fd5b8063a08cb48b1461067a578063a0cc6a681461068d578063a1088459146106b4575f80fd5b80639dc29fac116101785780639dc29fac1461064c5780639f8495f91461065f5780639fd5a6cf14610667575f80fd5b80638a75f2381461063c57806395d89b4114610644575f80fd5b80634c57a8fa116102525780637ecebe00116101fd57806384af270f116101d857806384af270f146105d657806384b0196e1461060e57806388b7ab6314610629575f80fd5b80637ecebe001461057d5780637f2eecc31461059c57806381399be4146105c3575f80fd5b8063578f2aa01161022d578063578f2aa01461053c5780635a049a701461055757806370a082311461056a575f80fd5b80634c57a8fa146104b2578063532992c5146104eb57806353d96f2c146104fe575f80fd5b8063281b229d116102b25780633644e5151161028d5780633644e5151461044957806340c10f191461045157806348545a3c14610466575f80fd5b8063281b229d1461039057806330adf81f146103e9578063313ce56714610410575f80fd5b806318160ddd116102e257806318160ddd1461033e57806323b872dd1461035457806326987b6014610367575f80fd5b806306fdde03146102fd578063095ea7b31461031b575b5f80fd5b61030561083a565b60405161031291906134bd565b60405180910390f35b61032e6103293660046134f7565b6108ca565b6040519015158152602001610312565b6103466108df565b604051908152602001610312565b61032e61036236600461351f565b610917565b61036f610a08565b6040516fffffffffffffffffffffffffffffffff9091168152602001610312565b6006546103ba907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6103467f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104377f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610312565b610346610a9a565b61046461045f3660046134f7565b610aef565b005b61048d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6007546104cc906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610312565b6104646104f9366004613558565b610b6c565b5f546105269074010000000000000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610312565b5f5461036f906fffffffffffffffffffffffffffffffff1681565b61046461056536600461359e565b610b91565b6103466105783660046135e8565b610bb8565b61034661058b3660046135e8565b60026020525f908152604090205481565b6103467fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104646105d13660046135e8565b610c68565b61032e6105e43660046135e8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526008602052604090205460ff1690565b610616610cb4565b6040516103129796959493929190613601565b610464610637366004613795565b610ded565b6103ba610e1c565b610305610e3a565b61046461065a3660046134f7565b610ec6565b610464610f3f565b610464610675366004613814565b610f4a565b610464610688366004613881565b610f5a565b6103467f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b61048d610f84565b610464610fae565b61048d7f000000000000000000000000000000000000000000000000000000000000000081565b61032e6106f93660046134f7565b610ff6565b61046461070c366004613881565b611002565b61046461071f3660046138e3565b611022565b61036f61103f565b5f54700100000000000000000000000000000000900463ffffffff1660405163ffffffff9091168152602001610312565b6103ba61076b3660046135e8565b61118f565b61046461077e366004613795565b611201565b610464610791366004613936565b611221565b6103467f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103466107cb36600461399b565b600560209081525f928352604080842090915290825290205481565b6104646107f53660046139cc565b611231565b61032e6108083660046134f7565b600360209081525f928352604080842090915290825290205460ff1681565b6104646108353660046139cc565b61125c565b60606001805461084990613a44565b80601f016020809104026020016040519081016040528092919081815260200182805461087590613a44565b80156108c05780601f10610897576101008083540402835291602001916108c0565b820191905f5260205f20905b8154815290600101906020018083116108a357829003601f168201915b5050505050905090565b5f6108d633848461127c565b50600192915050565b5f6108e8610e1c565b6006547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169190910116919050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526005602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109f257828110156109bd576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600560209081526040808320338452909152902083820390555b6109fd8585856112e9565b506001949350505050565b5f8054610a9590610a7c906fffffffffffffffffffffffffffffffff811690610a7790612710700100000000000000000000000000000000820463ffffffff1664e8d4a5100002049074010000000000000000000000000000000000000000900464ffffffffff164203611594565b6115bd565b71ffffffffffffffffffffffffffffffffffff166115ea565b905090565b5f7f00000000000000000000000000000000000000000000000000000000000000004614610aca57610a9561160b565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610b5e576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b6882826116a6565b5050565b610b8184610b7a86866118cb565b848461193f565b610b8b8484611953565b50505050565b610ba785610b9f87876118cb565b8585856119e1565b610bb18585611953565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff16610c1157805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610c41565b8054610c419061010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166119f1565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b610c7181611a03565b15610ca8576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb181611a5d565b50565b5f60608082808083600146308480604051908082528060200260200182016040528015610ceb578160200160208202803683370190505b507f0f000000000000000000000000000000000000000000000000000000000000009493929190848054610d1e90613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4a90613a44565b8015610d955780601f10610d6c57610100808354040283529160200191610d95565b820191905f5260205f20905b815481529060010190602001808311610d7857829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b610e0587610dff898989898989611c5f565b83611ce6565b610e13878787878787611d31565b50505050505050565b6007545f90610a95906dffffffffffffffffffffffffffff166119f1565b60048054610e4790613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610e7390613a44565b8015610ebe5780601f10610e9557610100808354040283529160200191610ebe565b820191905f5260205f20905b815481529060010190602001808311610ea157829003601f168201915b505050505081565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f35576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b688282611dac565b610f4833611a5d565b565b610bb185610dff87878787611e5e565b610f6c88610b7a8a8a8a8a8a8a611c5f565b610f7a888888888888611d31565b5050505050505050565b5f610a957f0000000000000000000000000000000000000000000000000000000000000000611f09565b610fb733611a03565b610fed576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f4833611f3a565b5f6108d63384846112e9565b61101488610b7a8a8a8a8a8a8a612109565b610f7a888888888888612185565b61103083610dff85856118cb565b61103a8383611953565b505050565b5f8061104961229e565b5f5490915064ffffffffff74010000000000000000000000000000000000000000909104164214801561109957505f5463ffffffff82811670010000000000000000000000000000000090920416145b156110b75750505f546fffffffffffffffffffffffffffffffff1690565b6110bf610a08565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff166111c2575f6111ea565b805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff165b6dffffffffffffffffffffffffffff169392505050565b61121387610dff898989898989612109565b610e13878787878787612185565b610e1387610b9f89898989611e5e565b61124389610b9f8b8b8b8b8b8b612109565b611251898989898989612185565b505050505050505050565b61126e89610b9f8b8b8b8b8b8b611c5f565b611251898989898989611d31565b73ffffffffffffffffffffffffffffffffffffffff8381165f8181526005602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6112f2826123a3565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161135191815260200190565b60405180910390a35f61136382612408565b73ffffffffffffffffffffffffffffffffffffffff8086165f9081526008602052604080822054928716825290205491925060ff90811691161515811515036113d457610bb18585836113b65784612477565b6113bf85612465565b6dffffffffffffffffffffffffffff16612477565b80156114a3576113ec856113e784612465565b6125e3565b73ffffffffffffffffffffffffffffffffffffffff84165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168601811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055611584565b6114ad8583612778565b611584846114ba846128ff565b73ffffffffffffffffffffffffffffffffffffffff919091165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116919091011690920217909155600780547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b61158c61103f565b505050505050565b5f6115b66301e1338067ffffffffffffffff851663ffffffff85160204612911565b9392505050565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff8416025b049392505050565b5f611605826fffffffffffffffffffffffffffffffff61298a565b92915050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161163e9190613ac2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6116af8161299f565b6116b8826123a3565b60405181815273ffffffffffffffffffffffffffffffffffffffff8316905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35f61170f82612408565b6006549091507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90811681831601118061179b57506006546dffffffffffffffffffffffffffff90611780907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168301612465565b6007546dffffffffffffffffffffffffffff90811691160110155b156117d2576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526008602052604090205460ff16156118145761180c836114ba836128ff565b610b8b61103f565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168501811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff841691810191909152606081018290525f906115b6906080015b604051602081830303815290604052805190602001206129db565b610b8b61194e85858585612a3c565b612a77565b61195d8282612c3b565b73ffffffffffffffffffffffffffffffffffffffff82165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610bb161194e8686868686612cc4565b5f611605826119fe610a08565b612d0c565b5f611a2d7f0000000000000000000000000000000000000000000000000000000000000000612d17565b8061160557506116057f000000000000000000000000000000000000000000000000000000000000000083612d4b565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff16611a8e575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016815573ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604081205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116916dffffffffffffffffffffffffffff9091169003611b5d57505050565b5f611b67826119f1565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808416610100027fff000000000000000000000000000000000000000000000000000000000000ff909216919091179091556006805480831684019092167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055600780546dffffffffffffffffffffffffffff808216869003167fffffffffffffffffffffffffffffffffffff00000000000000000000000000009091161790559050610bb161103f565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b979650505050505050565b5f611cf2848484612d77565b90505f816005811115611d0757611d07613b97565b03611d125750505050565b611d1d848484612dbb565b15611d285750505050565b610b8b81612a77565b3373ffffffffffffffffffffffffffffffffffffffff861614611d9e576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff861660248201526044016109b4565b61158c868686868686612185565b611db58161299f565b6040518181525f9073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205460ff1615611e4c57611e44826113e7611e3f84612408565b612465565b61103a61103f565b610b6882611e5983612408565b612778565b5f611e6882612f03565b611e7385858561127c565b73ffffffffffffffffffffffffffffffffffffffff8581165f8181526002602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052611f009060e001611924565b95945050505050565b5f611605611f37837f6561726e65725f726174655f6d6f64656c000000000000000000000000000000612f46565b90565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff1615611f6c575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082557dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100909104165f81900361200957505050565b5f612013826128ff565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8381166101000291909117909155600780547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081169083168401909216919091179055600680547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182168690039091161790559050610bb161103f565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b8242116121c7576040517f498ff9a2000000000000000000000000000000000000000000000000000000008152426004820152602481018490526044016109b4565b814210612209576040517fb3fcd33e000000000000000000000000000000000000000000000000000000008152426004820152602481018390526044016109b4565b6122138682612c3b565b73ffffffffffffffffffffffffffffffffffffffff86165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a361158c8686866112e9565b5f805f6122a9610f84565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916123259190613bc4565b5f60405180830381855afa9150503d805f811461235d576040519150601f19603f3d011682016040523d82523d5f602084013e612362565b606091505b509150915081801561237657506020815110155b612380575f61239c565b61239c818060200190518101906123979190613bdf565b612fd5565b9250505090565b73ffffffffffffffffffffffffffffffffffffffff8116610cb1576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016109b4565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612461576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f61160582612472610a08565b612fe4565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612544576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9283165f9081526008602052604080822080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff82168110156126ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290526dffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080546dffffffffffffffffffffffffffff8084167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560078054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612845576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560068054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b5f6116058261290c610a08565b612fef565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161298057612980613a95565b0495945050505050565b5f81831061299857816115b6565b5090919050565b805f03610cb1576040517f77b8dde3000000000000000000000000000000000000000000000000000000008152600481018290526024016109b4565b5f6129e4610a9a565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f805f612a4a868686612ffa565b90925090505f826005811115612a6257612a62613b97565b14612a6d5781611cdb565b611cdb8782613041565b5f816005811115612a8a57612a8a613b97565b03612a925750565b6001816005811115612aa657612aa6613b97565b03612add576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816005811115612af157612af1613b97565b03612b28576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003816005811115612b3c57612b3c613b97565b03612b73576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004816005811115612b8757612b87613b97565b03612bbe576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005816005811115612bd257612bd2613b97565b03612c09576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f90815260036020908152604080832084845290915290205460ff1615610b68576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602481018290526044016109b4565b5f805f612cd387878787613084565b90925090505f826005811115612ceb57612ceb613b97565b14612cf65781612d00565b612d008882613041565b98975050505050505050565b5f6115b68383613191565b5f80612d43837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612f46565b141592915050565b5f6115b6837f6561726e657273000000000000000000000000000000000000000000000000008461319c565b5f805f612d84858561323c565b90925090505f826005811115612d9c57612d9c613b97565b14612da75781612db1565b612db18682613041565b9695505050505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff168585604051602401612de8929190613bf6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612e699190613bc4565b5f60405180830381855afa9150503d805f8114612ea1576040519150601f19603f3d011682016040523d82523d5f602084013e612ea6565b606091505b5091509150818015612eba57506020815110155b8015612db1575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612ef89083016020908101908401613bdf565b149695505050505050565b80421115610cb1576040517ff88f0490000000000000000000000000000000000000000000000000000000008152600481018290524260248201526044016109b4565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015612fb1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115b69190613bdf565b5f6116058263ffffffff61298a565b5f6115b68383613280565b5f6115b68383613341565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841661303387838884613084565b935093505050935093915050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461307c5760056115b6565b505f92915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156130b95750600390505f613188565b8460ff16601b141580156130d157508460ff16601c14155b156130e15750600490505f613188565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613131573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161561317f575f81613183565b60015f5b915091505b94509492505050565b5f6115b683836133d8565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa158015613210573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132349190613c0e565b949350505050565b5f8082516041146132525750600290505f613279565b6020830151604084015160608501515f1a919061327187848484613084565b945094505050505b9250929050565b5f816fffffffffffffffffffffffffffffffff165f036132cc576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161333b5761333b613a95565b04613409565b5f816fffffffffffffffffffffffffffffffff165f0361338d576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161333b5761333b613a95565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026115e2565b5f6dffffffffffffffffffffffffffff821115612461576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561346c578181015183820152602001613454565b50505f910152565b5f815180845261348b816020860160208601613452565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f6115b66020830184613474565b803573ffffffffffffffffffffffffffffffffffffffff811681146134f2575f80fd5b919050565b5f8060408385031215613508575f80fd5b613511836134cf565b946020939093013593505050565b5f805f60608486031215613531575f80fd5b61353a846134cf565b9250613548602085016134cf565b9150604084013590509250925092565b5f805f806080858703121561356b575f80fd5b613574856134cf565b966020860135965060408601359560600135945092505050565b803560ff811681146134f2575f80fd5b5f805f805f60a086880312156135b2575f80fd5b6135bb866134cf565b9450602086013593506135d06040870161358e565b94979396509394606081013594506080013592915050565b5f602082840312156135f8575f80fd5b6115b6826134cf565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e0602084015261363d60e084018a613474565b838103604085015261364f818a613474565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b818110156136af57835183529284019291840191600101613693565b50909c9b505050505050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126136fd575f80fd5b813567ffffffffffffffff80821115613718576137186136c1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561375e5761375e6136c1565b81604052838152866020858801011115613776575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f805f805f60e0888a0312156137ab575f80fd5b6137b4886134cf565b96506137c2602089016134cf565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156137f9575f80fd5b6138058a828b016136ee565b91505092959891949750929550565b5f805f805f60a08688031215613828575f80fd5b613831866134cf565b945061383f602087016134cf565b93506040860135925060608601359150608086013567ffffffffffffffff811115613868575f80fd5b613874888289016136ee565b9150509295509295909350565b5f805f805f805f80610100898b031215613899575f80fd5b6138a2896134cf565b97506138b060208a016134cf565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b5f805f606084860312156138f5575f80fd5b6138fe846134cf565b925060208401359150604084013567ffffffffffffffff811115613920575f80fd5b61392c868287016136ee565b9150509250925092565b5f805f805f805f60e0888a03121561394c575f80fd5b613955886134cf565b9650613963602089016134cf565b9550604088013594506060880135935061397f6080890161358e565b925060a0880135915060c0880135905092959891949750929550565b5f80604083850312156139ac575f80fd5b6139b5836134cf565b91506139c3602084016134cf565b90509250929050565b5f805f805f805f805f6101208a8c0312156139e5575f80fd5b6139ee8a6134cf565b98506139fc60208b016134cf565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a2660c08b0161358e565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680613a5857607f821691505b602082108103613a8f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f8083545f60018260011c91506001831680613adf57607f831692505b60208084108203613b17577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b818015613b2b5760018114613b5e57613b89565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650613b89565b5f8a8152602090205f5b86811015613b815781548b820152908501908301613b68565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f8251613bd5818460208701613452565b9190910192915050565b5f60208284031215613bef575f80fd5b5051919050565b828152604060208201525f6132346040830184613474565b5f60208284031215613c1e575f80fd5b815180151581146115b6575f80fdfea2646970667358221220c687d1c508879cda1ed165e77e2f28932b2c0a517d37ea99537bab765838dc6564736f6c63430008170033000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106102f9575f3560e01c80638a75f23811610192578063ace150a5116100e8578063d505accf11610093578063e3ee160e1161006e578063e3ee160e146107e7578063e94a0102146107fa578063ef55bec614610827575f80fd5b8063d505accf14610783578063d916948714610796578063dd62ed3e146107bd575f80fd5b8063c23465b3116100c3578063c23465b31461072c578063c634dfaa1461075d578063cf09299514610770575f80fd5b8063ace150a5146106fe578063b7b7289914610711578063b9f412b014610724575f80fd5b8063a08cb48b11610148578063a36e40fc11610123578063a36e40fc146106bc578063a6ce63cd146106c4578063a9059cbb146106eb575f80fd5b8063a08cb48b1461067a578063a0cc6a681461068d578063a1088459146106b4575f80fd5b80639dc29fac116101785780639dc29fac1461064c5780639f8495f91461065f5780639fd5a6cf14610667575f80fd5b80638a75f2381461063c57806395d89b4114610644575f80fd5b80634c57a8fa116102525780637ecebe00116101fd57806384af270f116101d857806384af270f146105d657806384b0196e1461060e57806388b7ab6314610629575f80fd5b80637ecebe001461057d5780637f2eecc31461059c57806381399be4146105c3575f80fd5b8063578f2aa01161022d578063578f2aa01461053c5780635a049a701461055757806370a082311461056a575f80fd5b80634c57a8fa146104b2578063532992c5146104eb57806353d96f2c146104fe575f80fd5b8063281b229d116102b25780633644e5151161028d5780633644e5151461044957806340c10f191461045157806348545a3c14610466575f80fd5b8063281b229d1461039057806330adf81f146103e9578063313ce56714610410575f80fd5b806318160ddd116102e257806318160ddd1461033e57806323b872dd1461035457806326987b6014610367575f80fd5b806306fdde03146102fd578063095ea7b31461031b575b5f80fd5b61030561083a565b60405161031291906134bd565b60405180910390f35b61032e6103293660046134f7565b6108ca565b6040519015158152602001610312565b6103466108df565b604051908152602001610312565b61032e61036236600461351f565b610917565b61036f610a08565b6040516fffffffffffffffffffffffffffffffff9091168152602001610312565b6006546103ba907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6103467f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104377f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff9091168152602001610312565b610346610a9a565b61046461045f3660046134f7565b610aef565b005b61048d7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6007546104cc906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610312565b6104646104f9366004613558565b610b6c565b5f546105269074010000000000000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610312565b5f5461036f906fffffffffffffffffffffffffffffffff1681565b61046461056536600461359e565b610b91565b6103466105783660046135e8565b610bb8565b61034661058b3660046135e8565b60026020525f908152604090205481565b6103467fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104646105d13660046135e8565b610c68565b61032e6105e43660046135e8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526008602052604090205460ff1690565b610616610cb4565b6040516103129796959493929190613601565b610464610637366004613795565b610ded565b6103ba610e1c565b610305610e3a565b61046461065a3660046134f7565b610ec6565b610464610f3f565b610464610675366004613814565b610f4a565b610464610688366004613881565b610f5a565b6103467f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b61048d610f84565b610464610fae565b61048d7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b61032e6106f93660046134f7565b610ff6565b61046461070c366004613881565b611002565b61046461071f3660046138e3565b611022565b61036f61103f565b5f54700100000000000000000000000000000000900463ffffffff1660405163ffffffff9091168152602001610312565b6103ba61076b3660046135e8565b61118f565b61046461077e366004613795565b611201565b610464610791366004613936565b611221565b6103467f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103466107cb36600461399b565b600560209081525f928352604080842090915290825290205481565b6104646107f53660046139cc565b611231565b61032e6108083660046134f7565b600360209081525f928352604080842090915290825290205460ff1681565b6104646108353660046139cc565b61125c565b60606001805461084990613a44565b80601f016020809104026020016040519081016040528092919081815260200182805461087590613a44565b80156108c05780601f10610897576101008083540402835291602001916108c0565b820191905f5260205f20905b8154815290600101906020018083116108a357829003601f168201915b5050505050905090565b5f6108d633848461127c565b50600192915050565b5f6108e8610e1c565b6006547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169190910116919050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526005602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109f257828110156109bd576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600560209081526040808320338452909152902083820390555b6109fd8585856112e9565b506001949350505050565b5f8054610a9590610a7c906fffffffffffffffffffffffffffffffff811690610a7790612710700100000000000000000000000000000000820463ffffffff1664e8d4a5100002049074010000000000000000000000000000000000000000900464ffffffffff164203611594565b6115bd565b71ffffffffffffffffffffffffffffffffffff166115ea565b905090565b5f7f00000000000000000000000000000000000000000000000000000000000000014614610aca57610a9561160b565b507fc91b8161082e3755d12edaa80f168579bff06d439dd704fb764dfa4abd84134290565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e1614610b5e576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b6882826116a6565b5050565b610b8184610b7a86866118cb565b848461193f565b610b8b8484611953565b50505050565b610ba785610b9f87876118cb565b8585856119e1565b610bb18585611953565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff16610c1157805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610c41565b8054610c419061010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166119f1565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b610c7181611a03565b15610ca8576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb181611a5d565b50565b5f60608082808083600146308480604051908082528060200260200182016040528015610ceb578160200160208202803683370190505b507f0f000000000000000000000000000000000000000000000000000000000000009493929190848054610d1e90613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4a90613a44565b8015610d955780601f10610d6c57610100808354040283529160200191610d95565b820191905f5260205f20905b815481529060010190602001808311610d7857829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b610e0587610dff898989898989611c5f565b83611ce6565b610e13878787878787611d31565b50505050505050565b6007545f90610a95906dffffffffffffffffffffffffffff166119f1565b60048054610e4790613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610e7390613a44565b8015610ebe5780601f10610e9557610100808354040283529160200191610ebe565b820191905f5260205f20905b815481529060010190602001808311610ea157829003601f168201915b505050505081565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e1614610f35576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b688282611dac565b610f4833611a5d565b565b610bb185610dff87878787611e5e565b610f6c88610b7a8a8a8a8a8a8a611c5f565b610f7a888888888888611d31565b5050505050505050565b5f610a957f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c611f09565b610fb733611a03565b610fed576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f4833611f3a565b5f6108d63384846112e9565b61101488610b7a8a8a8a8a8a8a612109565b610f7a888888888888612185565b61103083610dff85856118cb565b61103a8383611953565b505050565b5f8061104961229e565b5f5490915064ffffffffff74010000000000000000000000000000000000000000909104164214801561109957505f5463ffffffff82811670010000000000000000000000000000000090920416145b156110b75750505f546fffffffffffffffffffffffffffffffff1690565b6110bf610a08565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff166111c2575f6111ea565b805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff165b6dffffffffffffffffffffffffffff169392505050565b61121387610dff898989898989612109565b610e13878787878787612185565b610e1387610b9f89898989611e5e565b61124389610b9f8b8b8b8b8b8b612109565b611251898989898989612185565b505050505050505050565b61126e89610b9f8b8b8b8b8b8b611c5f565b611251898989898989611d31565b73ffffffffffffffffffffffffffffffffffffffff8381165f8181526005602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6112f2826123a3565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161135191815260200190565b60405180910390a35f61136382612408565b73ffffffffffffffffffffffffffffffffffffffff8086165f9081526008602052604080822054928716825290205491925060ff90811691161515811515036113d457610bb18585836113b65784612477565b6113bf85612465565b6dffffffffffffffffffffffffffff16612477565b80156114a3576113ec856113e784612465565b6125e3565b73ffffffffffffffffffffffffffffffffffffffff84165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168601811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055611584565b6114ad8583612778565b611584846114ba846128ff565b73ffffffffffffffffffffffffffffffffffffffff919091165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116919091011690920217909155600780547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b61158c61103f565b505050505050565b5f6115b66301e1338067ffffffffffffffff851663ffffffff85160204612911565b9392505050565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff8416025b049392505050565b5f611605826fffffffffffffffffffffffffffffffff61298a565b92915050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161163e9190613ac2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6116af8161299f565b6116b8826123a3565b60405181815273ffffffffffffffffffffffffffffffffffffffff8316905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35f61170f82612408565b6006549091507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90811681831601118061179b57506006546dffffffffffffffffffffffffffff90611780907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168301612465565b6007546dffffffffffffffffffffffffffff90811691160110155b156117d2576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526008602052604090205460ff16156118145761180c836114ba836128ff565b610b8b61103f565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168501811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff841691810191909152606081018290525f906115b6906080015b604051602081830303815290604052805190602001206129db565b610b8b61194e85858585612a3c565b612a77565b61195d8282612c3b565b73ffffffffffffffffffffffffffffffffffffffff82165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610bb161194e8686868686612cc4565b5f611605826119fe610a08565b612d0c565b5f611a2d7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c612d17565b8061160557506116057f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c83612d4b565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff16611a8e575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016815573ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604081205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116916dffffffffffffffffffffffffffff9091169003611b5d57505050565b5f611b67826119f1565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808416610100027fff000000000000000000000000000000000000000000000000000000000000ff909216919091179091556006805480831684019092167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055600780546dffffffffffffffffffffffffffff808216869003167fffffffffffffffffffffffffffffffffffff00000000000000000000000000009091161790559050610bb161103f565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b979650505050505050565b5f611cf2848484612d77565b90505f816005811115611d0757611d07613b97565b03611d125750505050565b611d1d848484612dbb565b15611d285750505050565b610b8b81612a77565b3373ffffffffffffffffffffffffffffffffffffffff861614611d9e576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff861660248201526044016109b4565b61158c868686868686612185565b611db58161299f565b6040518181525f9073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205460ff1615611e4c57611e44826113e7611e3f84612408565b612465565b61103a61103f565b610b6882611e5983612408565b612778565b5f611e6882612f03565b611e7385858561127c565b73ffffffffffffffffffffffffffffffffffffffff8581165f8181526002602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052611f009060e001611924565b95945050505050565b5f611605611f37837f6561726e65725f726174655f6d6f64656c000000000000000000000000000000612f46565b90565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff1615611f6c575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082557dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100909104165f81900361200957505050565b5f612013826128ff565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8381166101000291909117909155600780547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081169083168401909216919091179055600680547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182168690039091161790559050610bb161103f565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b8242116121c7576040517f498ff9a2000000000000000000000000000000000000000000000000000000008152426004820152602481018490526044016109b4565b814210612209576040517fb3fcd33e000000000000000000000000000000000000000000000000000000008152426004820152602481018390526044016109b4565b6122138682612c3b565b73ffffffffffffffffffffffffffffffffffffffff86165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a361158c8686866112e9565b5f805f6122a9610f84565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916123259190613bc4565b5f60405180830381855afa9150503d805f811461235d576040519150601f19603f3d011682016040523d82523d5f602084013e612362565b606091505b509150915081801561237657506020815110155b612380575f61239c565b61239c818060200190518101906123979190613bdf565b612fd5565b9250505090565b73ffffffffffffffffffffffffffffffffffffffff8116610cb1576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016109b4565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612461576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f61160582612472610a08565b612fe4565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612544576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9283165f9081526008602052604080822080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff82168110156126ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290526dffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080546dffffffffffffffffffffffffffff8084167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560078054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612845576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560068054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b5f6116058261290c610a08565b612fef565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161298057612980613a95565b0495945050505050565b5f81831061299857816115b6565b5090919050565b805f03610cb1576040517f77b8dde3000000000000000000000000000000000000000000000000000000008152600481018290526024016109b4565b5f6129e4610a9a565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f805f612a4a868686612ffa565b90925090505f826005811115612a6257612a62613b97565b14612a6d5781611cdb565b611cdb8782613041565b5f816005811115612a8a57612a8a613b97565b03612a925750565b6001816005811115612aa657612aa6613b97565b03612add576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816005811115612af157612af1613b97565b03612b28576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003816005811115612b3c57612b3c613b97565b03612b73576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004816005811115612b8757612b87613b97565b03612bbe576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005816005811115612bd257612bd2613b97565b03612c09576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f90815260036020908152604080832084845290915290205460ff1615610b68576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602481018290526044016109b4565b5f805f612cd387878787613084565b90925090505f826005811115612ceb57612ceb613b97565b14612cf65781612d00565b612d008882613041565b98975050505050505050565b5f6115b68383613191565b5f80612d43837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612f46565b141592915050565b5f6115b6837f6561726e657273000000000000000000000000000000000000000000000000008461319c565b5f805f612d84858561323c565b90925090505f826005811115612d9c57612d9c613b97565b14612da75781612db1565b612db18682613041565b9695505050505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff168585604051602401612de8929190613bf6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612e699190613bc4565b5f60405180830381855afa9150503d805f8114612ea1576040519150601f19603f3d011682016040523d82523d5f602084013e612ea6565b606091505b5091509150818015612eba57506020815110155b8015612db1575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612ef89083016020908101908401613bdf565b149695505050505050565b80421115610cb1576040517ff88f0490000000000000000000000000000000000000000000000000000000008152600481018290524260248201526044016109b4565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015612fb1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115b69190613bdf565b5f6116058263ffffffff61298a565b5f6115b68383613280565b5f6115b68383613341565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841661303387838884613084565b935093505050935093915050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461307c5760056115b6565b505f92915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156130b95750600390505f613188565b8460ff16601b141580156130d157508460ff16601c14155b156130e15750600490505f613188565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613131573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161561317f575f81613183565b60015f5b915091505b94509492505050565b5f6115b683836133d8565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa158015613210573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132349190613c0e565b949350505050565b5f8082516041146132525750600290505f613279565b6020830151604084015160608501515f1a919061327187848484613084565b945094505050505b9250929050565b5f816fffffffffffffffffffffffffffffffff165f036132cc576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161333b5761333b613a95565b04613409565b5f816fffffffffffffffffffffffffffffffff165f0361338d576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161333b5761333b613a95565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026115e2565b5f6dffffffffffffffffffffffffffff821115612461576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561346c578181015183820152602001613454565b50505f910152565b5f815180845261348b816020860160208601613452565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f6115b66020830184613474565b803573ffffffffffffffffffffffffffffffffffffffff811681146134f2575f80fd5b919050565b5f8060408385031215613508575f80fd5b613511836134cf565b946020939093013593505050565b5f805f60608486031215613531575f80fd5b61353a846134cf565b9250613548602085016134cf565b9150604084013590509250925092565b5f805f806080858703121561356b575f80fd5b613574856134cf565b966020860135965060408601359560600135945092505050565b803560ff811681146134f2575f80fd5b5f805f805f60a086880312156135b2575f80fd5b6135bb866134cf565b9450602086013593506135d06040870161358e565b94979396509394606081013594506080013592915050565b5f602082840312156135f8575f80fd5b6115b6826134cf565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e0602084015261363d60e084018a613474565b838103604085015261364f818a613474565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b818110156136af57835183529284019291840191600101613693565b50909c9b505050505050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126136fd575f80fd5b813567ffffffffffffffff80821115613718576137186136c1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561375e5761375e6136c1565b81604052838152866020858801011115613776575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f805f805f60e0888a0312156137ab575f80fd5b6137b4886134cf565b96506137c2602089016134cf565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156137f9575f80fd5b6138058a828b016136ee565b91505092959891949750929550565b5f805f805f60a08688031215613828575f80fd5b613831866134cf565b945061383f602087016134cf565b93506040860135925060608601359150608086013567ffffffffffffffff811115613868575f80fd5b613874888289016136ee565b9150509295509295909350565b5f805f805f805f80610100898b031215613899575f80fd5b6138a2896134cf565b97506138b060208a016134cf565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b5f805f606084860312156138f5575f80fd5b6138fe846134cf565b925060208401359150604084013567ffffffffffffffff811115613920575f80fd5b61392c868287016136ee565b9150509250925092565b5f805f805f805f60e0888a03121561394c575f80fd5b613955886134cf565b9650613963602089016134cf565b9550604088013594506060880135935061397f6080890161358e565b925060a0880135915060c0880135905092959891949750929550565b5f80604083850312156139ac575f80fd5b6139b5836134cf565b91506139c3602084016134cf565b90509250929050565b5f805f805f805f805f6101208a8c0312156139e5575f80fd5b6139ee8a6134cf565b98506139fc60208b016134cf565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a2660c08b0161358e565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680613a5857607f821691505b602082108103613a8f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f8083545f60018260011c91506001831680613adf57607f831692505b60208084108203613b17577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b818015613b2b5760018114613b5e57613b89565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650613b89565b5f8a8152602090205f5b86811015613b815781548b820152908501908301613b68565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f8251613bd5818460208701613452565b9190910192915050565b5f60208284031215613bef575f80fd5b5051919050565b828152604060208201525f6132346040830184613474565b5f60208284031215613c1e575f80fd5b815180151581146115b6575f80fdfea2646970667358221220c687d1c508879cda1ed165e77e2f28932b2c0a517d37ea99537bab765838dc6564736f6c63430008170033

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

000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e

-----Decoded View---------------
Arg [0] : ttgRegistrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [1] : minterGateway_ (address): 0xf7f9638cb444D65e5A40bF5ff98ebE4ff319F04E

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

M^0 is an on-chain protocol, as well as a set of off-chain standards and APIs, that powers the first open federation of cryptodollar issuers. Minters on the federation connect to the protocol to mint the M token.

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.