Sepolia Testnet

Contract

0x4eDfcfB5F9e55962EF1A2eEf0b56A8FaDbaBA289

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Update Collatera...85324662025-06-12 12:04:365 days ago1749729876IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000216711.3037367
Burn M84906582025-06-06 16:31:2411 days ago1749227484IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000122991.00000057
Mint M84898842025-06-06 13:55:0011 days ago1749218100IN
0x4eDfcfB5...aDbaBA289
0 ETH0.0001381.00000065
Propose Mint84898462025-06-06 13:47:2411 days ago1749217644IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000096181.0000004
Update Collatera...84893052025-06-06 11:59:0011 days ago1749211140IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000160781.00000067
Update Collatera...84882722025-06-06 8:31:4811 days ago1749198708IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000166191.0000141
Update Collatera...84681902025-06-03 13:27:4814 days ago1748957268IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000150651.00000005
Mint M84663722025-06-03 7:24:1214 days ago1748935452IN
0x4eDfcfB5...aDbaBA289
0 ETH0.0001381.00000033
Propose Mint84663542025-06-03 7:20:3614 days ago1748935236IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000096181.00000027
Mint M84663252025-06-03 7:14:4814 days ago1748934888IN
0x4eDfcfB5...aDbaBA289
0 ETH0.00015511.00000008
Propose Mint84663052025-06-03 7:10:4814 days ago1748934648IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000144371.50100004
Propose Retrieva...84662692025-06-03 7:03:3614 days ago1748934216IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000079331.00000003
Mint M84662522025-06-03 7:00:1214 days ago1748934012IN
0x4eDfcfB5...aDbaBA289
0 ETH0.0001381.00000002
Propose Mint84662062025-06-03 6:51:0014 days ago1748933460IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000096181.00000002
Mint M84660992025-06-03 6:29:3614 days ago1748932176IN
0x4eDfcfB5...aDbaBA289
0 ETH0.0001381.00000001
Propose Mint84660802025-06-03 6:25:4814 days ago1748931948IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000059181.00000002
Burn M84660712025-06-03 6:24:0014 days ago1748931840IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000122991.00000002
Propose Mint84660692025-06-03 6:23:3614 days ago1748931816IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000061981.00000002
Update Collatera...84660602025-06-03 6:21:4814 days ago1748931708IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000160781.00000003
Update Collatera...84603882025-06-02 11:26:4815 days ago1748863608IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000160771.00000033
Update Collatera...84601632025-06-02 10:41:2415 days ago1748860884IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000160781.00000013
Update Collatera...84601622025-06-02 10:41:1215 days ago1748860872IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000166191.00000013
Update Collatera...84231102025-05-28 6:57:4820 days ago1748415468IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000158171.00139931
Burn M84177922025-05-27 13:12:0021 days ago1748351520IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000124231.01006897
Update Collatera...84163972025-05-27 8:32:4821 days ago1748334768IN
0x4eDfcfB5...aDbaBA289
0 ETH0.000161631.02330133
View all transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
View All Internal Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MinterGateway

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 999999 runs

Other Settings:
shanghai EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 19 : MinterGateway.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

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

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

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

/*

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

-->> Where money is born. <<--

*/

/**
 * @title  MinterGateway
 * @author M^0 Labs
 * @notice Minting Gateway of M Token for all approved by TTG and activated minters.
 */
contract MinterGateway is IMinterGateway, ContinuousIndexing, ERC712Extended {
    /* ============ Structs ============ */

    /**
     * @notice Mint proposal struct.
     * @param  id          The unique ID of the mint proposal.
     * @param  createdAt   The timestamp at which the mint proposal was created.
     * @param  destination The address to mint M to.
     * @param  amount      The amount of M to mint.
     */
    struct MintProposal {
        // 1st slot
        uint48 id;
        uint40 createdAt;
        address destination;
        // 2nd slot
        uint240 amount;
    }

    /**
     * @notice Minter state struct.
     * @param  isActive                Whether the minter is active or not.
     * @param  isDeactivated           Whether the minter is deactivated or not.
     * @param  collateral              The amount of collateral the minter has.
     * @param  totalPendingRetrievals  The total amount of pending retrievals.
     * @param  updateTimestamp         The timestamp at which the minter last updated their collateral.
     * @param  penalizedUntilTimestamp The timestamp until which the minter is penalized.
     * @param  frozenUntilTimestamp    The timestamp until which the minter is frozen.
     */
    struct MinterState {
        // 1st slot
        bool isActive;
        bool isDeactivated;
        uint240 collateral;
        // 2nd slot
        uint240 totalPendingRetrievals;
        // 3rd slot
        uint40 updateTimestamp;
        uint40 penalizedUntilTimestamp;
        uint40 frozenUntilTimestamp;
        uint40 latestProposedRetrievalTimestamp;
    }

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

    /// @inheritdoc IMinterGateway
    uint16 public constant ONE = 10_000;

    /// @inheritdoc IMinterGateway
    uint32 public constant MAX_MINT_RATIO = 65_000;

    /// @notice IMinterGateway
    uint32 public constant MIN_UPDATE_COLLATERAL_INTERVAL = 3_600;

    // solhint-disable-next-line max-line-length
    /// @dev keccak256("UpdateCollateral(address minter,uint256 collateral,uint256[] retrievalIds,bytes32 metadataHash,uint256 timestamp)")
    /// @inheritdoc IMinterGateway
    bytes32 public constant UPDATE_COLLATERAL_TYPEHASH =
        0x22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e;

    /// @inheritdoc IMinterGateway
    address public immutable ttgRegistrar;

    /// @inheritdoc IMinterGateway
    address public immutable ttgVault;

    /// @inheritdoc IMinterGateway
    address public immutable mToken;

    /// @inheritdoc IMinterGateway
    uint240 public totalInactiveOwedM;

    /// @inheritdoc IMinterGateway
    uint112 public principalOfTotalActiveOwedM;

    /// @dev Nonce used to generate unique mint proposal IDs.
    uint48 internal _mintNonce;

    /// @dev Nonce used to generate unique retrieval proposal IDs.
    uint48 internal _retrievalNonce;

    /// @dev The state of each minter, their collaterals, relevant timestamps, and total pending retrievals.
    mapping(address minter => MinterState state) internal _minterStates;

    /// @dev The mint proposals of minter (mint ID, creation timestamp, destination, amount).
    mapping(address minter => MintProposal proposal) internal _mintProposals;

    /// @dev The owed M of active and inactive minters (principal of active, inactive).
    mapping(address minter => uint240 rawOwedM) internal _rawOwedM;

    /// @dev The pending collateral retrievals of minter (retrieval ID, amount).
    mapping(address minter => mapping(uint48 retrievalId => uint240 amount)) internal _pendingCollateralRetrievals;

    /// @dev The last update signature timestamp of each validator for each minter.
    mapping(address minter => mapping(address validator => uint256 timestamp)) internal _lastSignatureTimestamp;

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

    /**
     * @notice Only allow active minter to call function.
     * @param  minter_ The address of the minter to check.
     */
    modifier onlyActiveMinter(address minter_) {
        _revertIfInactiveMinter(minter_);

        _;
    }

    /// @notice Only allow approved validator in TTG to call function.
    modifier onlyApprovedValidator() {
        _revertIfNotApprovedValidator(msg.sender);

        _;
    }

    /// @notice Only allow unfrozen minter to call function.
    modifier onlyUnfrozenMinter() {
        _revertIfFrozenMinter(msg.sender);

        _;
    }

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

    /**
     * @notice Constructor.
     * @param  ttgRegistrar_ The address of the TTG Registrar contract.
     * @param  mToken_        The address of the M Token.
     */
    constructor(address ttgRegistrar_, address mToken_) ContinuousIndexing() ERC712Extended("MinterGateway") {
        if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar();
        if ((ttgVault = TTGRegistrarReader.getVault(ttgRegistrar_)) == address(0)) revert ZeroTTGVault();
        if ((mToken = mToken_) == address(0)) revert ZeroMToken();
    }

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

    /// @inheritdoc IMinterGateway
    function updateCollateral(
        uint256 collateral_,
        uint256[] calldata retrievalIds_,
        bytes32 metadataHash_,
        address[] calldata validators_,
        uint256[] calldata timestamps_,
        bytes[] calldata signatures_
    ) external onlyActiveMinter(msg.sender) returns (uint40 minTimestamp_) {
        if (validators_.length != signatures_.length || signatures_.length != timestamps_.length) {
            revert SignatureArrayLengthsMismatch();
        }

        // Verify that enough valid signatures are provided, and get the minimum timestamp across all valid signatures.
        minTimestamp_ = _verifyValidatorSignatures(
            msg.sender,
            collateral_,
            retrievalIds_,
            metadataHash_,
            validators_,
            timestamps_,
            signatures_
        );

        uint240 safeCollateral_ = UIntMath.safe240(collateral_);
        uint240 totalResolvedCollateralRetrieval_ = _resolvePendingRetrievals(msg.sender, retrievalIds_);

        emit CollateralUpdated(
            msg.sender,
            safeCollateral_,
            totalResolvedCollateralRetrieval_,
            metadataHash_,
            minTimestamp_
        );

        _imposePenaltyIfMissedCollateralUpdates(msg.sender);

        _imposePenaltyIfUndercollateralized(msg.sender, minTimestamp_);

        _updateCollateral(msg.sender, safeCollateral_, minTimestamp_);

        // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the collateral
        //       update can result in a new rate, we should update the index here to lock in that rate.
        updateIndex();
    }

    /// @inheritdoc IMinterGateway
    function proposeRetrieval(uint256 collateral_) external onlyActiveMinter(msg.sender) returns (uint48 retrievalId_) {
        if (collateral_ == 0) revert ZeroRetrievalAmount();

        unchecked {
            retrievalId_ = ++_retrievalNonce;
        }

        MinterState storage minterState_ = _minterStates[msg.sender];
        uint240 currentCollateral_ = minterState_.collateral;
        uint240 safeCollateral_ = UIntMath.safe240(collateral_);
        uint240 updatedTotalPendingRetrievals_ = minterState_.totalPendingRetrievals + safeCollateral_;

        // NOTE: Revert if collateral is less than sum of all pending retrievals even if there is no owed M by minter.
        if (currentCollateral_ < updatedTotalPendingRetrievals_) {
            revert RetrievalsExceedCollateral(updatedTotalPendingRetrievals_, currentCollateral_);
        }

        minterState_.latestProposedRetrievalTimestamp = uint40(block.timestamp);
        minterState_.totalPendingRetrievals = updatedTotalPendingRetrievals_;
        _pendingCollateralRetrievals[msg.sender][retrievalId_] = safeCollateral_;

        _revertIfUndercollateralized(msg.sender, 0);

        emit RetrievalCreated(retrievalId_, msg.sender, safeCollateral_);
    }

    /// @inheritdoc IMinterGateway
    function proposeMint(
        uint256 amount_,
        address destination_
    ) external onlyActiveMinter(msg.sender) onlyUnfrozenMinter returns (uint48 mintId_) {
        if (amount_ == 0) revert ZeroMintAmount();
        if (destination_ == address(0)) revert ZeroMintDestination();

        uint240 safeAmount_ = UIntMath.safe240(amount_);

        _revertIfUndercollateralized(msg.sender, safeAmount_); // Ensure minter remains sufficiently collateralized.

        unchecked {
            mintId_ = ++_mintNonce;
        }

        _mintProposals[msg.sender] = MintProposal(mintId_, uint40(block.timestamp), destination_, safeAmount_);

        emit MintProposed(mintId_, msg.sender, safeAmount_, destination_);
    }

    /// @inheritdoc IMinterGateway
    function mintM(
        uint256 mintId_
    ) external onlyActiveMinter(msg.sender) onlyUnfrozenMinter returns (uint112 principalAmount_, uint240 amount_) {
        MintProposal storage mintProposal_ = _mintProposals[msg.sender];

        uint48 id_;
        uint40 createdAt_;
        address destination_;
        (id_, createdAt_, destination_, amount_) = (
            mintProposal_.id,
            mintProposal_.createdAt,
            mintProposal_.destination,
            mintProposal_.amount
        );

        if (id_ != mintId_) revert InvalidMintProposal();

        unchecked {
            // Check that mint proposal is executable.
            uint40 activeAt_ = createdAt_ + mintDelay();
            if (block.timestamp < activeAt_) revert PendingMintProposal(activeAt_);

            uint40 expiresAt_ = activeAt_ + mintTTL();
            if (block.timestamp > expiresAt_) revert ExpiredMintProposal(expiresAt_);
        }

        _revertIfUndercollateralized(msg.sender, amount_); // Ensure minter remains sufficiently collateralized.

        delete _mintProposals[msg.sender]; // Delete mint request.

        // Adjust principal of active owed M for minter.
        // NOTE: When minting a present amount, round the principal up in favor of the protocol.
        principalAmount_ = _getPrincipalAmountRoundedUp(amount_);
        uint112 principalOfTotalActiveOwedM_ = principalOfTotalActiveOwedM;

        emit MintExecuted(id_, msg.sender, principalAmount_, amount_);

        unchecked {
            uint256 newPrincipalOfTotalActiveOwedM_ = uint256(principalOfTotalActiveOwedM_) + principalAmount_;

            // As an edge case precaution, prevent a mint that, if all owed M (active and inactive) was converted to
            // a principal active amount, would overflow the `uint112 principalOfTotalActiveOwedM`.
            if (
                // NOTE: Round the principal up for worst case.
                newPrincipalOfTotalActiveOwedM_ + _getPrincipalAmountRoundedUp(totalInactiveOwedM) >= type(uint112).max
            ) {
                revert OverflowsPrincipalOfTotalOwedM();
            }

            principalOfTotalActiveOwedM = uint112(newPrincipalOfTotalActiveOwedM_);
            _rawOwedM[msg.sender] += principalAmount_; // Treat rawOwedM as principal since minter is active.
        }

        IMToken(mToken).mint(destination_, amount_);

        // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the mint
        //       can result in a new rate, we should update the index here to lock in that rate.
        updateIndex();
    }

    /// @inheritdoc IMinterGateway
    function burnM(address minter_, uint256 maxAmount_) external returns (uint112 principalAmount_, uint240 amount_) {
        (principalAmount_, amount_) = burnM(
            minter_,
            _getPrincipalAmountRoundedDown(UIntMath.safe240(maxAmount_)),
            maxAmount_
        );
    }

    /// @inheritdoc IMinterGateway
    function burnM(
        address minter_,
        uint256 maxPrincipalAmount_,
        uint256 maxAmount_
    ) public returns (uint112 principalAmount_, uint240 amount_) {
        if (maxPrincipalAmount_ == 0 || maxAmount_ == 0) revert ZeroBurnAmount();

        MinterState storage minterState_ = _minterStates[minter_];
        bool isActive_ = minterState_.isActive;

        // Revert early if minter has not been activated.
        if (!isActive_ && !minterState_.isDeactivated) revert InactiveMinter();

        if (isActive_) {
            // NOTE: Penalize only for missed collateral updates, not for undercollateralization.
            // Undercollateralization within one update interval is forgiven.
            _imposePenaltyIfMissedCollateralUpdates(minter_);

            (principalAmount_, amount_) = _repayForActiveMinter(
                minter_,
                UIntMath.safe112(maxPrincipalAmount_),
                UIntMath.safe240(maxAmount_)
            );

            emit BurnExecuted(minter_, principalAmount_, amount_, msg.sender);
        } else {
            amount_ = _repayForDeactivatedMinter(minter_, UIntMath.safe240(maxAmount_));

            emit BurnExecuted(minter_, amount_, msg.sender);
        }

        IMToken(mToken).burn(msg.sender, amount_); // Burn actual M tokens

        // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the burn
        //       can result in a new rate, we should update the index here to lock in that rate.
        updateIndex();
    }

    /// @inheritdoc IMinterGateway
    function cancelMint(address minter_, uint256 mintId_) external onlyApprovedValidator {
        uint48 id_ = _mintProposals[minter_].id;

        if (id_ != mintId_ || id_ == 0) revert InvalidMintProposal();

        delete _mintProposals[minter_];

        emit MintCanceled(id_, minter_, msg.sender);
    }

    /// @inheritdoc IMinterGateway
    function freezeMinter(address minter_) external onlyApprovedValidator returns (uint40 frozenUntil_) {
        unchecked {
            _minterStates[minter_].frozenUntilTimestamp = frozenUntil_ = uint40(block.timestamp) + minterFreezeTime();
        }

        emit MinterFrozen(minter_, frozenUntil_);
    }

    /// @inheritdoc IMinterGateway
    function activateMinter(address minter_) external {
        if (!isMinterApproved(minter_)) revert NotApprovedMinter();

        MinterState storage minterState_ = _minterStates[minter_];

        if (minterState_.isDeactivated) revert DeactivatedMinter();

        minterState_.isActive = true;

        emit MinterActivated(minter_, msg.sender);
    }

    /// @inheritdoc IMinterGateway
    function deactivateMinter(address minter_) external onlyActiveMinter(minter_) returns (uint240 inactiveOwedM_) {
        if (isMinterApproved(minter_)) revert StillApprovedMinter();

        _imposePenaltyIfMissedCollateralUpdates(minter_);

        uint112 principalOfOwedM_ = principalOfActiveOwedMOf(minter_);

        inactiveOwedM_ = _getPresentAmount(principalOfOwedM_);

        unchecked {
            // Treat rawOwedM as principal since minter is active.
            principalOfTotalActiveOwedM -= principalOfOwedM_;
            totalInactiveOwedM += inactiveOwedM_;
        }

        emit MinterDeactivated(minter_, inactiveOwedM_, msg.sender);

        // Reset reasonable aspects of minter's state
        delete _minterStates[minter_];
        delete _mintProposals[minter_];

        // Deactivate minter.
        _minterStates[minter_].isDeactivated = true;

        _rawOwedM[minter_] = inactiveOwedM_; // Treat rawOwedM as inactive owed M since minter is now inactive.

        // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the
        //       deactivation can result in a new rate, we should update the index here to lock in that rate.
        updateIndex();
    }

    /// @inheritdoc IContinuousIndexing
    function updateIndex() public override(IContinuousIndexing, ContinuousIndexing) returns (uint128 index_) {
        // NOTE: Since the currentIndex of the Minter Gateway and mToken are constant through this context's execution
        //       (the block.timestamp is not changing) we can compute excessOwedM without updating the mToken index.
        uint240 excessOwedM_ = excessOwedM();

        if (excessOwedM_ > 0) IMToken(mToken).mint(ttgVault, excessOwedM_); // Mint M to TTG Vault.

        // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the collateral
        //       update can result in a new rate, we should update the index here to lock in that rate.
        // NOTE: With the current rate models, the minter rate does not depend on anything in the Minter Gateway
        //       or mToken, so we can update the minter rate and index here.
        index_ = super.updateIndex(); // Update minter index and rate.

        // NOTE: Given the current implementation of the mToken transfers and its rate model, while it is possible for
        //       the above mint to already have updated the mToken index if M was minted to an earning account, we want
        //       to ensure the rate provided by the mToken's rate model is locked in.
        IMToken(mToken).updateIndex(); // Update earning index and rate.
    }

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

    /// @inheritdoc IMinterGateway
    function totalActiveOwedM() public view returns (uint240) {
        return _getPresentAmount(principalOfTotalActiveOwedM);
    }

    /// @inheritdoc IMinterGateway
    function totalOwedM() external view returns (uint240) {
        unchecked {
            // NOTE: This can never overflow since the `mint` functions caps the principal of total owed M (active and
            //       inactive) to `type(uint112).max`. Thus, there can never be enough inactive owed M (which is an
            //       accumulations principal of active owed M values converted to present values at previous and lower
            //       indices) or active owed M to overflow this.
            return totalActiveOwedM() + totalInactiveOwedM;
        }
    }

    /// @inheritdoc IMinterGateway
    function excessOwedM() public view returns (uint240 excessOwedM_) {
        // NOTE: Can safely cast to `uint240` since we know M Token totalSupply constraints.
        uint240 totalMSupply_ = uint240(IMToken(mToken).totalSupply());

        uint240 totalOwedM_ = _getPresentAmountRoundedDown(principalOfTotalActiveOwedM, currentIndex()) +
            totalInactiveOwedM;

        unchecked {
            if (totalOwedM_ > totalMSupply_) return totalOwedM_ - totalMSupply_;
        }
    }

    /// @inheritdoc IMinterGateway
    function minterRate() external view returns (uint32) {
        return _latestRate;
    }

    /// @inheritdoc IMinterGateway
    function isActiveMinter(address minter_) external view returns (bool) {
        return _minterStates[minter_].isActive;
    }

    /// @inheritdoc IMinterGateway
    function isDeactivatedMinter(address minter_) external view returns (bool) {
        return _minterStates[minter_].isDeactivated;
    }

    /// @inheritdoc IMinterGateway
    function isFrozenMinter(address minter_) external view returns (bool) {
        return block.timestamp < _minterStates[minter_].frozenUntilTimestamp;
    }

    /// @inheritdoc IMinterGateway
    function principalOfActiveOwedMOf(address minter_) public view returns (uint112) {
        // NOTE: This should also include the principal value of unavoidable penalities. But then it would be very, if
        //       not impossible, to determine the `principalOfTotalActiveOwedM` to the same standards.
        return
            _minterStates[minter_].isActive
                ? uint112(_rawOwedM[minter_]) // Treat rawOwedM as principal since minter is active.
                : 0;
    }

    /// @inheritdoc IMinterGateway
    function activeOwedMOf(address minter_) public view returns (uint240) {
        // NOTE: This should also include the present value of unavoidable penalities. But then it would be very, if
        //       not impossible, to determine the `totalActiveOwedM` to the same standards.
        return
            _minterStates[minter_].isActive
                ? _getPresentAmount(uint112(_rawOwedM[minter_])) // Treat rawOwedM as principal since minter is active.
                : 0;
    }

    /// @inheritdoc IMinterGateway
    function maxAllowedActiveOwedMOf(address minter_) public view returns (uint256) {
        // NOTE: Since `mintRatio()` is capped at 650% (i.e. 65_000) this cannot overflow.
        unchecked {
            return _minterStates[minter_].isActive ? (uint256(collateralOf(minter_)) * mintRatio()) / ONE : 0;
        }
    }

    /// @inheritdoc IMinterGateway
    function inactiveOwedMOf(address minter_) public view returns (uint240) {
        // Treat rawOwedM as present amount since minter is inactive.
        return _minterStates[minter_].isActive ? 0 : _rawOwedM[minter_];
    }

    /// @inheritdoc IMinterGateway
    function collateralOf(address minter_) public view returns (uint240) {
        // If collateral was not updated by the deadline, assume that minter's collateral is zero.
        if (block.timestamp >= collateralExpiryTimestampOf(minter_)) return 0;

        MinterState storage minterState_ = _minterStates[minter_];
        uint240 totalPendingRetrievals_ = minterState_.totalPendingRetrievals;
        uint240 collateral_ = minterState_.collateral;

        // If the minter's total pending retrievals is greater than their collateral, then their collateral is zero.
        if (totalPendingRetrievals_ >= collateral_) return 0;

        unchecked {
            return collateral_ - totalPendingRetrievals_;
        }
    }

    /// @inheritdoc IMinterGateway
    function collateralUpdateTimestampOf(address minter_) external view returns (uint40) {
        return _minterStates[minter_].updateTimestamp;
    }

    /// @inheritdoc IMinterGateway
    function collateralPenaltyDeadlineOf(address minter_) external view returns (uint40) {
        MinterState storage minterState_ = _minterStates[minter_];
        uint32 updateCollateralInterval_ = updateCollateralInterval();

        (, uint40 missedUntil_) = _getMissedCollateralUpdateParameters(
            minterState_.updateTimestamp,
            minterState_.penalizedUntilTimestamp,
            updateCollateralInterval_
        );

        return missedUntil_ + updateCollateralInterval_;
    }

    /// @inheritdoc IMinterGateway
    function collateralExpiryTimestampOf(address minter_) public view returns (uint40) {
        unchecked {
            return _minterStates[minter_].updateTimestamp + updateCollateralInterval();
        }
    }

    /// @inheritdoc IMinterGateway
    function penalizedUntilOf(address minter_) external view returns (uint40) {
        return _minterStates[minter_].penalizedUntilTimestamp;
    }

    /// @inheritdoc IMinterGateway
    function latestProposedRetrievalTimestampOf(address minter_) external view returns (uint40) {
        return _minterStates[minter_].latestProposedRetrievalTimestamp;
    }

    /// @inheritdoc IMinterGateway
    function getLastSignatureTimestamp(address minter_, address validator_) external view returns (uint256) {
        return _lastSignatureTimestamp[minter_][validator_];
    }

    /// @inheritdoc IMinterGateway
    function getUpdateCollateralDigest(
        address minter_,
        uint256 collateral_,
        uint256[] calldata retrievalIds_,
        bytes32 metadataHash_,
        uint256 timestamp_
    ) external view returns (bytes32) {
        return _getUpdateCollateralDigest(minter_, collateral_, retrievalIds_, metadataHash_, timestamp_);
    }

    /// @inheritdoc IMinterGateway
    function mintProposalOf(
        address minter_
    ) external view returns (uint48 mintId_, uint40 createdAt_, address destination_, uint240 amount_) {
        mintId_ = _mintProposals[minter_].id;
        createdAt_ = _mintProposals[minter_].createdAt;
        destination_ = _mintProposals[minter_].destination;
        amount_ = _mintProposals[minter_].amount;
    }

    /// @inheritdoc IMinterGateway
    function pendingCollateralRetrievalOf(address minter_, uint256 retrievalId_) external view returns (uint240) {
        return
            _minterStates[minter_].isDeactivated
                ? 0
                : _pendingCollateralRetrievals[minter_][UIntMath.safe48(retrievalId_)];
    }

    /// @inheritdoc IMinterGateway
    function totalPendingCollateralRetrievalOf(address minter_) external view returns (uint240) {
        return _minterStates[minter_].isDeactivated ? 0 : _minterStates[minter_].totalPendingRetrievals;
    }

    /// @inheritdoc IMinterGateway
    function frozenUntilOf(address minter_) external view returns (uint40) {
        return _minterStates[minter_].frozenUntilTimestamp;
    }

    /* ============ TTG Registrar Reader Functions ============ */

    /// @inheritdoc IMinterGateway
    function isMinterApproved(address minter_) public view returns (bool) {
        return TTGRegistrarReader.isApprovedMinter(ttgRegistrar, minter_);
    }

    /// @inheritdoc IMinterGateway
    function isValidatorApproved(address validator_) public view returns (bool) {
        return TTGRegistrarReader.isApprovedValidator(ttgRegistrar, validator_);
    }

    /// @inheritdoc IMinterGateway
    function updateCollateralInterval() public view returns (uint32) {
        return
            UIntMath.max32(
                UIntMath.bound32(TTGRegistrarReader.getUpdateCollateralInterval(ttgRegistrar)),
                MIN_UPDATE_COLLATERAL_INTERVAL
            );
    }

    /// @inheritdoc IMinterGateway
    function updateCollateralValidatorThreshold() public view returns (uint256) {
        return TTGRegistrarReader.getUpdateCollateralValidatorThreshold(ttgRegistrar);
    }

    /// @inheritdoc IMinterGateway
    function mintRatio() public view returns (uint32) {
        // NOTE: It is possible for the mint ratio to be greater than 100%, but capped at 650%.
        return UIntMath.min32(MAX_MINT_RATIO, UIntMath.bound32(TTGRegistrarReader.getMintRatio(ttgRegistrar)));
    }

    /// @inheritdoc IMinterGateway
    function mintDelay() public view returns (uint32) {
        return UIntMath.bound32(TTGRegistrarReader.getMintDelay(ttgRegistrar));
    }

    /// @inheritdoc IMinterGateway
    function mintTTL() public view returns (uint32) {
        return UIntMath.bound32(TTGRegistrarReader.getMintTTL(ttgRegistrar));
    }

    /// @inheritdoc IMinterGateway
    function minterFreezeTime() public view returns (uint32) {
        return UIntMath.bound32(TTGRegistrarReader.getMinterFreezeTime(ttgRegistrar));
    }

    /// @inheritdoc IMinterGateway
    function penaltyRate() public view returns (uint32) {
        return UIntMath.bound32(TTGRegistrarReader.getPenaltyRate(ttgRegistrar));
    }

    /// @inheritdoc IMinterGateway
    function rateModel() public view returns (address) {
        return TTGRegistrarReader.getMinterRateModel(ttgRegistrar);
    }

    /// @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.multiplyIndicesUp(
                        latestIndex,
                        ContinuousIndexingMath.getContinuousIndex(
                            ContinuousIndexingMath.convertFromBasisPoints(_latestRate),
                            uint32(block.timestamp - latestUpdateTimestamp)
                        )
                    )
                );
        }
    }

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

    /**
     * @dev    Imposes penalty on an active minter. Calling this for an inactive minter will break accounting.
     * @param  minter_                 The address of the minter.
     * @param  principalOfPenaltyBase_ The principal of the base for penalization.
     * @return The principal of the imposed penalty.
     */
    function _imposePenalty(address minter_, uint152 principalOfPenaltyBase_) internal returns (uint112) {
        if (principalOfPenaltyBase_ == 0) return 0;

        uint32 penaltyRate_ = penaltyRate();

        if (penaltyRate_ == 0) return 0;

        unchecked {
            uint256 penaltyPrincipal_ = (uint256(principalOfPenaltyBase_) * penaltyRate_) / ONE;

            // As an edge case precaution, cap the penalty principal such that the resulting principal of total active
            // owed M plus the penalty principal is not greater than the max uint112.
            uint256 newPrincipalOfTotalActiveOwedM_ = principalOfTotalActiveOwedM + penaltyPrincipal_;

            if (newPrincipalOfTotalActiveOwedM_ > type(uint112).max) {
                penaltyPrincipal_ = type(uint112).max - principalOfTotalActiveOwedM;
                newPrincipalOfTotalActiveOwedM_ = type(uint112).max;
            }

            // Calculate and add penalty principal to total minter's principal of active owed M
            principalOfTotalActiveOwedM = uint112(newPrincipalOfTotalActiveOwedM_);

            _rawOwedM[minter_] += uint112(penaltyPrincipal_); // Treat rawOwedM as principal since minter is active.

            return uint112(penaltyPrincipal_);
        }
    }

    /**
     * @dev   Imposes penalty if minter missed collateral updates.
     * @param minter_ The address of the minter.
     */
    function _imposePenaltyIfMissedCollateralUpdates(address minter_) internal {
        MinterState storage minterState_ = _minterStates[minter_];

        (uint40 missedIntervals_, uint40 missedUntil_) = _getMissedCollateralUpdateParameters(
            minterState_.updateTimestamp,
            minterState_.penalizedUntilTimestamp,
            updateCollateralInterval()
        );

        if (missedIntervals_ == 0) return;

        // Save until when the minter has been penalized for missed intervals to prevent double penalizing them.
        minterState_.penalizedUntilTimestamp = missedUntil_;

        uint112 principalOfActiveOwedM_ = principalOfActiveOwedMOf(minter_);

        if (principalOfActiveOwedM_ == 0) return;

        uint112 penaltyPrincipal_ = _imposePenalty(minter_, uint152(principalOfActiveOwedM_) * missedIntervals_);

        if (penaltyPrincipal_ == 0) return;

        emit MissedIntervalsPenaltyImposed(minter_, missedIntervals_, _getPresentAmount(penaltyPrincipal_));
    }

    /**
     * @dev   Imposes penalty if minter is undercollateralized.
     * @param minter_       The address of the minter.
     * @param newTimestamp_ The timestamp of the collateral update.
     */
    function _imposePenaltyIfUndercollateralized(address minter_, uint40 newTimestamp_) internal {
        uint112 principalOfActiveOwedM_ = principalOfActiveOwedMOf(minter_);

        if (principalOfActiveOwedM_ == 0) return;

        uint256 maxAllowedActiveOwedM_ = maxAllowedActiveOwedMOf(minter_);

        // If the minter's max allowed active owed M is greater than `type(uint240).max`, then it's definitely greater
        // than the max possible active owed M for the minter, which is capped at `type(uint240).max`.
        if (maxAllowedActiveOwedM_ >= type(uint240).max) return;

        // NOTE: Round the principal down in favor of the protocol since this is a max applied to the minter.
        uint112 principalOfMaxAllowedActiveOwedM_ = _getPrincipalAmountRoundedDown(uint240(maxAllowedActiveOwedM_));

        // If the minter is not undercollateralized, then no penalty is imposed.
        if (principalOfMaxAllowedActiveOwedM_ >= principalOfActiveOwedM_) return;

        MinterState storage minterState_ = _minterStates[minter_];

        uint40 penalizeFrom_ = UIntMath.max40(minterState_.updateTimestamp, minterState_.penalizedUntilTimestamp);

        if (newTimestamp_ <= penalizeFrom_) return;

        unchecked {
            uint40 timeSpan_ = newTimestamp_ - penalizeFrom_;

            uint112 principalOfExcessOwedM_ = principalOfActiveOwedM_ - principalOfMaxAllowedActiveOwedM_;

            // NOTE: `newTimestamp_ - penalizeFrom_` will never be larger than `updateCollateralInterval_` since this
            //       function is only called after `_imposePenaltyIfMissedCollateralUpdates`, which ensures that the
            //       `penalizedUntilTimestamp` is within one `updateCollateralInterval_` of the `newTimestamp_`.
            //
            // NOTE: `updateCollateralInterval()` never equals 0, so the division is safe.
            //       Its minimum is capped at `MIN_UPDATE_COLLATERAL_INTERVAL`.
            uint112 penaltyPrincipal_ = _imposePenalty(
                minter_,
                (principalOfExcessOwedM_ * timeSpan_) / updateCollateralInterval()
            );

            if (penaltyPrincipal_ == 0) return;

            emit UndercollateralizedPenaltyImposed(
                minter_,
                _getPresentAmount(principalOfExcessOwedM_),
                timeSpan_,
                _getPresentAmount(penaltyPrincipal_)
            );
        }
    }

    /**
     * @dev    Repays active minter's owed M.
     * @param  minter_             The address of the minter.
     * @param  maxPrincipalAmount_ The maximum principal amount of active owed M to repay.
     * @param  maxAmount_          The maximum amount of active owed M to repay.
     * @return principalAmount_    The principal amount of active owed M that was actually repaid.
     * @return amount_             The amount of active owed M that was actually repaid.
     */
    function _repayForActiveMinter(
        address minter_,
        uint112 maxPrincipalAmount_,
        uint240 maxAmount_
    ) internal returns (uint112 principalAmount_, uint240 amount_) {
        principalAmount_ = UIntMath.min112(principalOfActiveOwedMOf(minter_), maxPrincipalAmount_);
        amount_ = _getPresentAmount(principalAmount_);

        if (amount_ > maxAmount_) revert ExceedsMaxRepayAmount(amount_, maxAmount_);

        unchecked {
            // Treat rawOwedM as principal since `principalAmount_` would only be non-zero for an active minter.
            _rawOwedM[minter_] -= principalAmount_;
            principalOfTotalActiveOwedM -= principalAmount_;
        }
    }

    /**
     * @dev    Repays deactivated minter's owed M.
     * @param  minter_    The address of the minter.
     * @param  maxAmount_ The maximum amount of inactive owed M to repay.
     * @return amount_    The amount of inactive owed M that was actually repaid.
     */
    function _repayForDeactivatedMinter(address minter_, uint240 maxAmount_) internal returns (uint240 amount_) {
        amount_ = UIntMath.min240(inactiveOwedMOf(minter_), maxAmount_);

        unchecked {
            // Treat rawOwedM as present amount since `amount_` would only be non-zero for an inactive minter.
            _rawOwedM[minter_] -= amount_;
            totalInactiveOwedM -= amount_;
        }
    }

    /**
     * @dev    Resolves the collateral retrieval IDs and updates the total pending collateral retrieval amount.
     * @param  minter_                           The address of the minter.
     * @param  retrievalIds_                     The list of outstanding collateral retrieval IDs to resolve.
     * @return totalResolvedCollateralRetrieval_ The total amount of collateral retrieval resolved.
     */
    function _resolvePendingRetrievals(
        address minter_,
        uint256[] calldata retrievalIds_
    ) internal returns (uint240 totalResolvedCollateralRetrieval_) {
        for (uint256 index_; index_ < retrievalIds_.length; ++index_) {
            uint48 retrievalId_ = UIntMath.safe48(retrievalIds_[index_]);
            uint240 pendingCollateralRetrieval_ = _pendingCollateralRetrievals[minter_][retrievalId_];

            if (pendingCollateralRetrieval_ == 0) continue;

            unchecked {
                // NOTE: The `proposeRetrieval` function already ensures that the sum of all
                // `_pendingCollateralRetrievals` is not larger than `type(uint240).max`.
                totalResolvedCollateralRetrieval_ += pendingCollateralRetrieval_;
            }

            delete _pendingCollateralRetrievals[minter_][retrievalId_];

            emit RetrievalResolved(retrievalId_, minter_);
        }

        unchecked {
            // NOTE: The `proposeRetrieval` function already ensures that `totalPendingRetrievals` is the sum of all
            // `_pendingCollateralRetrievals`.
            _minterStates[minter_].totalPendingRetrievals -= totalResolvedCollateralRetrieval_;
        }
    }

    /**
     * @dev   Updates the collateral amount and update timestamp for the minter.
     * @param minter_       The address of the minter.
     * @param amount_       The amount of collateral.
     * @param newTimestamp_ The timestamp of the collateral update.
     */
    function _updateCollateral(address minter_, uint240 amount_, uint40 newTimestamp_) internal {
        MinterState storage minterState_ = _minterStates[minter_];

        // The earliest allowed timestamp for a collateral update is the maximum of:
        //   - the last update timestamp,
        //   - the latest proposed retrieval timestamp, and
        //   - the current timestamp minus the update collateral interval.
        unchecked {
            // NOTE: Cannot underflow since `min40` is applied when `updateCollateralInterval()` > `block.timestamp`.
            uint40 earliestAllowedTimestamp_ = UIntMath.max40(
                UIntMath.max40(minterState_.updateTimestamp, minterState_.latestProposedRetrievalTimestamp),
                uint40(block.timestamp) - UIntMath.min40(updateCollateralInterval(), uint40(block.timestamp))
            );

            if (newTimestamp_ <= earliestAllowedTimestamp_) {
                revert StaleCollateralUpdate(newTimestamp_, earliestAllowedTimestamp_);
            }
        }

        minterState_.collateral = amount_;
        minterState_.updateTimestamp = newTimestamp_;
    }

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

    /**
     * @dev    Returns the penalization base and the penalized until timestamp.
     * @param  lastUpdateTimestamp_ The last timestamp at which the minter updated their collateral.
     * @param  lastPenalizedUntil_  The timestamp before which the minter shouldn't be penalized for missed updates.
     * @param  updateInterval_      The update collateral interval.
     * @return missedIntervals_     The number of missed update intervals.
     * @return missedUntil_         The timestamp until which `missedIntervals_` covers,
     *                              even if `missedIntervals_` is 0.
     */
    function _getMissedCollateralUpdateParameters(
        uint40 lastUpdateTimestamp_,
        uint40 lastPenalizedUntil_,
        uint32 updateInterval_
    ) internal view returns (uint40 missedIntervals_, uint40 missedUntil_) {
        uint40 penalizeFrom_ = UIntMath.max40(lastUpdateTimestamp_, lastPenalizedUntil_);

        // If brand new minter then there is no missed interval charge at all.
        if (lastUpdateTimestamp_ == 0) return (0, penalizeFrom_);

        uint40 timeElapsed_ = uint40(block.timestamp) - penalizeFrom_;

        if (timeElapsed_ < updateInterval_) return (0, penalizeFrom_);

        unchecked {
            // NOTE: `updateInterval_` never equals 0, so the division is safe.
            //       Its minimum is capped at `MIN_UPDATE_COLLATERAL_INTERVAL`.
            missedIntervals_ = timeElapsed_ / updateInterval_;

            // NOTE: Cannot really overflow a `uint40` since `missedIntervals_ * updateInterval_ <= timeElapsed_`.
            missedUntil_ = penalizeFrom_ + (missedIntervals_ * updateInterval_);
        }
    }

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

    /**
     * @dev    Returns the EIP-712 digest for updateCollateral method.
     * @param  minter_       The address of the minter.
     * @param  collateral_   The amount of collateral.
     * @param  retrievalIds_ The list of outstanding collateral retrieval IDs to resolve.
     * @param  metadataHash_ The hash of metadata of the collateral update, reserved for future informational use.
     * @param  timestamp_    The timestamp of the collateral update.
     * @return The EIP-712 digest.
     */
    function _getUpdateCollateralDigest(
        address minter_,
        uint256 collateral_,
        uint256[] calldata retrievalIds_,
        bytes32 metadataHash_,
        uint256 timestamp_
    ) internal view returns (bytes32) {
        return
            _getDigest(
                keccak256(
                    abi.encode(
                        UPDATE_COLLATERAL_TYPEHASH,
                        minter_,
                        collateral_,
                        keccak256(abi.encodePacked(retrievalIds_)),
                        metadataHash_,
                        timestamp_
                    )
                )
            );
    }

    /// @dev Returns the current rate from the rate model contract.
    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 minter is frozen by validator.
     * @param minter_ The address of the minter
     */
    function _revertIfFrozenMinter(address minter_) internal view {
        if (block.timestamp < _minterStates[minter_].frozenUntilTimestamp) revert FrozenMinter();
    }

    /**
     * @dev   Reverts if minter is inactive.
     * @param minter_ The address of the minter
     */
    function _revertIfInactiveMinter(address minter_) internal view {
        if (!_minterStates[minter_].isActive) revert InactiveMinter();
    }

    /**
     * @dev   Reverts if validator is not approved.
     * @param validator_ The address of the validator
     */
    function _revertIfNotApprovedValidator(address validator_) internal view {
        if (!isValidatorApproved(validator_)) revert NotApprovedValidator(validator_);
    }

    /**
     * @dev   Reverts if minter position will be undercollateralized after changes.
     * @param minter_          The address of the minter
     * @param additionalOwedM_ The amount of additional owed M the action will add to minter's position
     */
    function _revertIfUndercollateralized(address minter_, uint240 additionalOwedM_) internal view {
        uint256 maxAllowedActiveOwedM_ = maxAllowedActiveOwedMOf(minter_);

        // If the minter's max allowed active owed M is greater than the max uint240, then it's definitely greater than
        // the max possible active owed M for the minter, which is capped at the max uint240.
        if (maxAllowedActiveOwedM_ >= type(uint240).max) return;

        unchecked {
            uint256 finalActiveOwedM_ = uint256(activeOwedMOf(minter_)) + additionalOwedM_;

            if (finalActiveOwedM_ > maxAllowedActiveOwedM_) {
                revert Undercollateralized(finalActiveOwedM_, maxAllowedActiveOwedM_);
            }
        }
    }

    /**
     * @dev    Checks that enough valid unique signatures were provided.
     * @param  minter_       The address of the minter.
     * @param  collateral_   The amount of collateral.
     * @param  retrievalIds_ The list of outstanding collateral retrieval IDs to resolve.
     * @param  metadataHash_ The hash of metadata of the collateral update, reserved for future informational use.
     * @param  validators_   The list of validators.
     * @param  timestamps_   The list of validator timestamps for the collateral update signatures.
     * @param  signatures_   The list of signatures.
     * @return minTimestamp_ The minimum timestamp across all valid timestamps with valid signatures.
     */
    function _verifyValidatorSignatures(
        address minter_,
        uint256 collateral_,
        uint256[] calldata retrievalIds_,
        bytes32 metadataHash_,
        address[] calldata validators_,
        uint256[] calldata timestamps_,
        bytes[] calldata signatures_
    ) internal returns (uint40 minTimestamp_) {
        minTimestamp_ = uint40(block.timestamp);

        uint256 validCount_;

        for (uint256 index_; index_ < signatures_.length; ++index_) {
            unchecked {
                // Check that validator address is unique and not accounted for
                // NOTE: We revert here because this failure is entirely within the minter's control.
                if (index_ > 0 && validators_[index_] <= validators_[index_ - 1]) revert InvalidSignatureOrder();
            }

            if (
                !_verifyValidatorSignature(
                    minter_,
                    collateral_,
                    retrievalIds_,
                    metadataHash_,
                    validators_[index_],
                    timestamps_[index_],
                    signatures_[index_]
                )
            ) continue;

            // Find minimum between all valid timestamps for valid signatures.
            minTimestamp_ = UIntMath.min40(minTimestamp_, uint40(timestamps_[index_]));

            unchecked {
                ++validCount_;
            }
        }

        uint256 requiredThreshold_ = updateCollateralValidatorThreshold();

        if (validCount_ < requiredThreshold_) revert NotEnoughValidSignatures(validCount_, requiredThreshold_);
    }

    /**
     * @dev    Checks that a signature is a valid validator signature.
     * @param  minter_       The address of the minter.
     * @param  collateral_   The amount of collateral.
     * @param  retrievalIds_ The list of outstanding collateral retrieval IDs to resolve.
     * @param  metadataHash_ The hash of metadata of the collateral update, reserved for future informational use.
     * @param  validator_    The address of a validator.
     * @param  timestamp_    The timestamp for the collateral update signature.
     * @param  signature_    The signature from the validator.
     * @return Whether the signature is a valid validator signature or not.
     */
    function _verifyValidatorSignature(
        address minter_,
        uint256 collateral_,
        uint256[] calldata retrievalIds_,
        bytes32 metadataHash_,
        address validator_,
        uint256 timestamp_,
        bytes calldata signature_
    ) internal returns (bool) {
        // Check that the timestamp is not 0.
        // NOTE: Revert here because this failure is entirely within the minter's control.
        if (timestamp_ == 0) revert ZeroTimestamp();

        // Check that the timestamp is not in the future.
        // NOTE: Revert here because this failure is entirely within the minter's control.
        if (timestamp_ > uint40(block.timestamp)) revert FutureTimestamp();

        uint256 lastTimestamp_ = _lastSignatureTimestamp[minter_][validator_];

        // Check that the timestamp is not older than the last signature timestamp.
        // NOTE: Revert here because this failure is entirely within the minter's control.
        if (timestamp_ <= lastTimestamp_) revert OutdatedValidatorTimestamp(validator_, timestamp_, lastTimestamp_);

        // Check that validator is approved by TTG.
        if (!isValidatorApproved(validator_)) return false;

        // Check that ECDSA or ERC1271 signatures for given digest are valid.
        if (
            !SignatureChecker.isValidSignature(
                validator_,
                _getUpdateCollateralDigest(minter_, collateral_, retrievalIds_, metadataHash_, timestamp_),
                signature_
            )
        ) return false;

        // Save the last signature timestamp for the minter and validator combination.
        _lastSignatureTimestamp[minter_][validator_] = timestamp_;

        return true;
    }
}

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

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

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

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

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

// 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 19 : IMinterGateway.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

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

/**
 * @title  Minter Gateway Interface.
 * @author M^0 Labs
 */
interface IMinterGateway is IContinuousIndexing, IERC712 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when M tokens are burned and an inactive minter's owed M balance decreased.
     * @param  minter The address of the minter.
     * @param  amount The amount of M tokens burned.
     * @param  payer  The address of the payer.
     */
    event BurnExecuted(address indexed minter, uint240 amount, address indexed payer);

    /**
     * @notice Emitted when M tokens are burned and an active minter's owed M balance decreased.
     * @param  minter          The address of the minter.
     * @param  principalAmount The principal amount of M tokens burned.
     * @param  amount          The amount of M tokens burned.
     * @param  payer           The address of the payer.
     */
    event BurnExecuted(address indexed minter, uint112 principalAmount, uint240 amount, address indexed payer);

    /**
     * @notice Emitted when a minter's collateral is updated.
     * @param  minter                           Address of the minter
     * @param  collateral                       The latest amount of collateral
     * @param  totalResolvedCollateralRetrieval The total collateral amount of outstanding retrievals resolved.
     * @param  metadataHash                     The hash of some metadata reserved for future informational use.
     * @param  timestamp                        The timestamp of the collateral update,
     *                                          minimum of given validators' signatures.
     */
    event CollateralUpdated(
        address indexed minter,
        uint240 collateral,
        uint240 totalResolvedCollateralRetrieval,
        bytes32 indexed metadataHash,
        uint40 timestamp
    );

    /**
     * @notice Emitted when a minter is activated.
     * @param  minter Address of the minter that was activated
     * @param  caller Address who called the function
     */
    event MinterActivated(address indexed minter, address indexed caller);

    /**
     * @notice Emitted when a minter is deactivated.
     * @param  minter        Address of the minter that was deactivated.
     * @param  inactiveOwedM Amount of M tokens owed by the minter (in an inactive state).
     * @param  caller        Address who called the function.
     */
    event MinterDeactivated(address indexed minter, uint240 inactiveOwedM, address indexed caller);

    /**
     * @notice Emitted when a minter is frozen.
     * @param  minter      Address of the minter that was frozen
     * @param  frozenUntil Timestamp until the minter is frozen
     */
    event MinterFrozen(address indexed minter, uint40 frozenUntil);

    /**
     * @notice Emitted when a mint proposal is canceled.
     * @param  mintId    The id of the canceled mint proposal.
     * @param  minter    The address of the minter for which the mint was canceled.
     * @param  canceller The address of the validator who canceled the mint proposal.
     */
    event MintCanceled(uint48 indexed mintId, address indexed minter, address indexed canceller);

    /**
     * @notice Emitted when a mint proposal is executed.
     * @param  mintId          The id of the executed mint proposal.
     * @param  minter          The address of the minter that executed the mint.
     * @param  principalAmount The principal amount of M tokens minted.
     * @param  amount          The amount of M tokens minted.
     */
    event MintExecuted(uint48 indexed mintId, address indexed minter, uint112 principalAmount, uint240 amount);

    /**
     * @notice Emitted when a mint proposal is created.
     * @param  mintId      The id of mint proposal.
     * @param  minter      The address of the minter that proposed the mint.
     * @param  amount      The amount of M tokens to mint.
     * @param  destination The address to mint to.
     */
    event MintProposed(uint48 indexed mintId, address indexed minter, uint240 amount, address indexed destination);

    /**
     * @notice Emitted when a penalty is imposed on `minter` for missed update collateral intervals.
     * @param  minter          The address of the minter.
     * @param  missedIntervals The number of update intervals missed.
     * @param  penaltyAmount   The present amount of penalty charge.
     */
    event MissedIntervalsPenaltyImposed(address indexed minter, uint40 missedIntervals, uint240 penaltyAmount);

    /**
     * @notice Emitted when a penalty is imposed on `minter` for undercollateralization.
     * @param  minter        The address of the minter.
     * @param  excessOwedM   The present amount of owed M in excess of allowed owed M.
     * @param  timeSpan      The span of time over which the undercollateralization penalty was applied.
     * @param  penaltyAmount The present amount of penalty charge.
     */
    event UndercollateralizedPenaltyImposed(
        address indexed minter,
        uint240 excessOwedM,
        uint40 timeSpan,
        uint240 penaltyAmount
    );

    /**
     * @notice Emitted when a collateral retrieval proposal is created.
     * @param  retrievalId The id of retrieval proposal.
     * @param  minter      The address of the minter.
     * @param  amount      The amount of collateral to retrieve.
     */
    event RetrievalCreated(uint48 indexed retrievalId, address indexed minter, uint240 amount);

    /**
     * @notice Emitted when a collateral retrieval proposal is resolved.
     * @param  retrievalId The id of retrieval proposal.
     * @param  minter      The address of the minter.
     */
    event RetrievalResolved(uint48 indexed retrievalId, address indexed minter);

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

    /// @notice Emitted when calling `activateMinter` with a minter who was previously deactivated.
    error DeactivatedMinter();

    /// @notice Emitted when repay will burn more M than the repay specified.
    error ExceedsMaxRepayAmount(uint240 amount, uint240 maxAmount);

    /// @notice Emitted when calling `mintM` with a proposal that was created more than `mintDelay + mintTTL` time ago.
    error ExpiredMintProposal(uint40 deadline);

    /// @notice Emitted when calling `mintM` or `proposeMint` by a minter who was frozen by validator.
    error FrozenMinter();

    /// @notice Emitted when calling `updateCollateral` with any validator timestamp in the future.
    error FutureTimestamp();

    /// @notice Emitted when calling a function only allowed for active minters.
    error InactiveMinter();

    /// @notice Emitted when calling `cancelMint` or `mintM` with invalid `mintId`.
    error InvalidMintProposal();

    /// @notice Emitted when calling `updateCollateral` if `validators` addresses are not ordered in ascending order.
    error InvalidSignatureOrder();

    /// @notice Emitted when calling `activateMinter` if minter was not approved by TTG.
    error NotApprovedMinter();

    /// @notice Emitted when calling `cancelMint` or `freezeMinter` if `validator` was not approved by TTG.
    error NotApprovedValidator(address validator);

    /// @notice Emitted when calling `updateCollateral` if `validatorThreshold` of signatures was not reached.
    error NotEnoughValidSignatures(uint256 validSignatures, uint256 requiredThreshold);

    /// @notice Emitted when principal of total owed M (active and inactive) will overflow a `type(uint112).max`.
    error OverflowsPrincipalOfTotalOwedM();

    /// @notice Emitted when calling `mintM` if `mintDelay` time has not passed yet.
    error PendingMintProposal(uint40 activeTimestamp);

    /// @notice Emitted when calling `proposeRetrieval` if sum of all outstanding retrievals
    ///         Plus new proposed retrieval amount is greater than collateral.
    error RetrievalsExceedCollateral(uint240 totalPendingRetrievals, uint240 collateral);

    /// @notice Emitted when calling `updateCollateral`
    ///         If `validators`, `signatures`, `timestamps` lengths do not match.
    error SignatureArrayLengthsMismatch();

    /// @notice Emitted when updating collateral with a timestamp earlier than allowed.
    error StaleCollateralUpdate(uint40 newTimestamp, uint40 earliestAllowedTimestamp);

    /// @notice Emitted when calling `updateCollateral` with any validator timestamp older than the last signature
    ///         timestamp for that minter and validator.
    error OutdatedValidatorTimestamp(address validator, uint256 timestamp, uint256 lastSignatureTimestamp);

    /// @notice Emitted when calling `deactivateMinter` with a minter still approved in TTG Registrar.
    error StillApprovedMinter();

    /**
     * @notice Emitted when calling `proposeMint`, `mintM`, `proposeRetrieval`
     *         If minter position becomes undercollateralized.
     * @dev    `activeOwedM` is a `uint256` because it may represent some resulting owed M from computations.
     */
    error Undercollateralized(uint256 activeOwedM, uint256 maxAllowedOwedM);

    /// @notice Emitted when calling `burnM` if amount is 0.
    error ZeroBurnAmount();

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

    /// @notice Emitted when calling `proposeMint` if amount is 0.
    error ZeroMintAmount();

    /// @notice Emitted when calling `proposeMint` if destination is 0x0.
    error ZeroMintDestination();

    /// @notice Emitted when calling `proposeRetrieval` if collateral is 0.
    error ZeroRetrievalAmount();

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

    /// @notice Emitted in constructor if TTG Distribution Vault is set to 0x0 in TTG Registrar.
    error ZeroTTGVault();

    /// @notice Emitted when calling `updateCollateral` with any validator timestamp of 0.
    error ZeroTimestamp();

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

    /**
     * @notice Updates collateral for minters
     * @param  collateral   The amount of collateral
     * @param  retrievalIds The list of active proposeRetrieval requests to close
     * @param  metadataHash The hash of metadata of the collateral update, reserved for future informational use
     * @param  validators   The list of validators
     * @param  timestamps   The list of timestamps of validators' signatures
     * @param  signatures   The list of signatures
     * @return minTimestamp The minimum timestamp of all validators' signatures
     */
    function updateCollateral(
        uint256 collateral,
        uint256[] calldata retrievalIds,
        bytes32 metadataHash,
        address[] calldata validators,
        uint256[] calldata timestamps,
        bytes[] calldata signatures
    ) external returns (uint40 minTimestamp);

    /**
     * @notice Proposes retrieval of minter's off-chain collateral
     * @param  collateral  The amount of collateral to retrieve
     * @return retrievalId The unique id of created retrieval proposal
     */
    function proposeRetrieval(uint256 collateral) external returns (uint48 retrievalId);

    /**
     * @notice Proposes minting of M tokens
     * @param  amount      The amount of M tokens to mint
     * @param  destination The address to mint to
     * @return mintId      The unique id of created mint proposal
     */
    function proposeMint(uint256 amount, address destination) external returns (uint48 mintId);

    /**
     * @notice Executes minting of M tokens
     * @param  mintId          The id of outstanding mint proposal for minter
     * @return principalAmount The amount of principal of owed M minted.
     * @return amount          The amount of M tokens minted.
     */
    function mintM(uint256 mintId) external returns (uint112 principalAmount, uint240 amount);

    /**
     * @notice Burns M tokens
     * @dev    If amount to burn is greater than minter's owedM including penalties, burn all up to owedM.
     * @param  minter          The address of the minter to burn M tokens for.
     * @param  maxAmount       The max amount of M tokens to burn.
     * @return principalAmount The amount of principal of owed M burned.
     * @return amount          The amount of M tokens burned.
     */
    function burnM(address minter, uint256 maxAmount) external returns (uint112 principalAmount, uint240 amount);

    /**
     * @notice Burns M tokens
     * @dev    If amount to burn is greater than minter's owedM including penalties, burn all up to owedM.
     * @param  minter             The address of the minter to burn M tokens for.
     * @param  maxPrincipalAmount The max amount of principal of owed M to burn.
     * @param  maxAmount          The max amount of M tokens to burn.
     * @return principalAmount    The amount of principal of owed M burned.
     * @return amount             The amount of M tokens burned.
     */
    function burnM(
        address minter,
        uint256 maxPrincipalAmount,
        uint256 maxAmount
    ) external returns (uint112 principalAmount, uint240 amount);

    /**
     * @notice Cancels minting request for selected minter by validator
     * @param  minter The address of the minter to cancelMint minting request for
     * @param  mintId The id of outstanding mint request
     */
    function cancelMint(address minter, uint256 mintId) external;

    /**
     * @notice Freezes minter
     * @param  minter      The address of the minter to freeze
     * @return frozenUntil The timestamp until which minter is frozen
     */
    function freezeMinter(address minter) external returns (uint40 frozenUntil);

    /**
     * @notice Activate an approved minter.
     * @dev    MUST revert if `minter` is not recorded as an approved minter in TTG Registrar.
     * @dev    MUST revert if `minter` has been deactivated.
     * @param  minter The address of the minter to activate
     */
    function activateMinter(address minter) external;

    /**
     * @notice Deactivates an active minter.
     * @dev    MUST revert if the minter is still approved.
     * @dev    MUST revert if the minter is not active.
     * @param  minter        The address of the minter to deactivate.
     * @return inactiveOwedM The inactive owed M for the deactivated minter.
     */
    function deactivateMinter(address minter) external returns (uint240 inactiveOwedM);

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

    /// @notice The address of M token
    function mToken() external view returns (address);

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

    /// @notice The address of TTG Vault contract.
    function ttgVault() external view returns (address);

    /// @notice The last saved value of Minter rate.
    function minterRate() external view returns (uint32);

    /// @notice The principal of total owed M for all active minters.
    function principalOfTotalActiveOwedM() external view returns (uint112);

    /// @notice The total owed M for all active minters.
    function totalActiveOwedM() external view returns (uint240);

    /// @notice The total owed M for all inactive minters.
    function totalInactiveOwedM() external view returns (uint240);

    /// @notice The total owed M for all minters.
    function totalOwedM() external view returns (uint240);

    /// @notice The difference between total owed M and M token total supply.
    function excessOwedM() external view returns (uint240);

    /// @notice The principal of active owed M of minter.
    function principalOfActiveOwedMOf(address minter_) external view returns (uint112);

    /// @notice The active owed M of minter.
    function activeOwedMOf(address minter) external view returns (uint240);

    /**
     * @notice The max allowed active owed M of minter taking into account collateral amount and retrieval proposals.
     * @dev    This is the only present value that requires a `uint256` since it is the result of a multiplication
     *         between a `uint240` and a value that has a max of `65,000` (the mint ratio).
     */
    function maxAllowedActiveOwedMOf(address minter) external view returns (uint256);

    /// @notice The inactive owed M of deactivated minter.
    function inactiveOwedMOf(address minter) external view returns (uint240);

    /// @notice The collateral of a given minter.
    function collateralOf(address minter) external view returns (uint240);

    /// @notice The timestamp of the last collateral update of minter.
    function collateralUpdateTimestampOf(address minter) external view returns (uint40);

    /// @notice The timestamp after which an additional penalty for a missed update interval will be charged.
    function collateralPenaltyDeadlineOf(address minter) external view returns (uint40);

    /// @notice The timestamp after which the minter's collateral is assumed to be 0 due to a missed update.
    function collateralExpiryTimestampOf(address minter) external view returns (uint40);

    /// @notice The timestamp until which minter is already penalized for missed collateral updates.
    function penalizedUntilOf(address minter) external view returns (uint40);

    /// @notice The timestamp when `minter` created their latest retrieval proposal.
    function latestProposedRetrievalTimestampOf(address minter) external view returns (uint40);

    /**
     * @notice Returns the last signature timestamp used by `validator` to update collateral for `minter`.
     * @param  minter    The address of the minter.
     * @param  validator The address of the validator.
     * @return The last signature timestamp used.
     */
    function getLastSignatureTimestamp(address minter, address validator) external view returns (uint256);

    /**
     * @notice Returns the EIP-712 digest for updateCollateral method.
     * @param  minter       The address of the minter.
     * @param  collateral   The amount of collateral.
     * @param  retrievalIds The list of outstanding collateral retrieval IDs to resolve.
     * @param  metadataHash The hash of metadata of the collateral update, reserved for future informational use.
     * @param  timestamp    The timestamp of the collateral update.
     */
    function getUpdateCollateralDigest(
        address minter,
        uint256 collateral,
        uint256[] calldata retrievalIds,
        bytes32 metadataHash,
        uint256 timestamp
    ) external view returns (bytes32);

    /// @notice The mint proposal of minters, only 1 active proposal per minter
    function mintProposalOf(
        address minter
    ) external view returns (uint48 mintId, uint40 createdAt, address destination, uint240 amount);

    /// @notice The amount of a pending retrieval request for an active minter.
    function pendingCollateralRetrievalOf(address minter, uint256 retrievalId) external view returns (uint240);

    /// @notice The total amount of pending retrieval requests for an active minter.
    function totalPendingCollateralRetrievalOf(address minter) external view returns (uint240);

    /// @notice The timestamp when minter becomes unfrozen after being frozen by validator.
    function frozenUntilOf(address minter) external view returns (uint40);

    /// @notice Checks if minter was activated after approval by TTG
    function isActiveMinter(address minter) external view returns (bool);

    /// @notice Checks if minter was deactivated after removal by TTG
    function isDeactivatedMinter(address minter) external view returns (bool);

    /// @notice Checks if minter was frozen by validator
    function isFrozenMinter(address minter) external view returns (bool);

    /// @notice Checks if minter was approved by TTG
    function isMinterApproved(address minter) external view returns (bool);

    /// @notice Checks if validator was approved by TTG
    function isValidatorApproved(address validator) external view returns (bool);

    /// @notice The delay between mint proposal creation and its earliest execution.
    function mintDelay() external view returns (uint32);

    /// @notice The time while mint request can still be processed before it is considered expired.
    function mintTTL() external view returns (uint32);

    /// @notice The freeze time for minter.
    function minterFreezeTime() external view returns (uint32);

    /// @notice The allowed activeOwedM to collateral ratio.
    function mintRatio() external view returns (uint32);

    /// @notice The % that defines penalty amount for missed collateral updates or excessive owedM value
    function penaltyRate() external view returns (uint32);

    /// @notice The smart contract that defines the minter rate.
    function rateModel() external view returns (address);

    /// @notice The interval that defines the required frequency of collateral updates.
    function updateCollateralInterval() external view returns (uint32);

    /// @notice The number of signatures required for successful collateral update.
    function updateCollateralValidatorThreshold() external view returns (uint256);

    /// @notice Descaler for variables in basis points. Effectively, 100% in basis points.
    function ONE() external pure returns (uint16);

    /// @notice Mint ratio cap. 650% in basis points.
    function MAX_MINT_RATIO() external pure returns (uint32);

    /// @notice Update collateral interval lower cap in seconds.
    function MIN_UPDATE_COLLATERAL_INTERVAL() external pure returns (uint32);

    /// @notice The EIP-712 typehash for the `updateCollateral` method.
    function UPDATE_COLLATERAL_TYPEHASH() external pure returns (bytes32);
}

File 9 of 19 : 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);
}

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

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

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

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

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

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

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

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

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

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

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 ABI

API
[{"inputs":[{"internalType":"address","name":"ttgRegistrar_","type":"address"},{"internalType":"address","name":"mToken_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeactivatedMinter","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[{"internalType":"uint240","name":"amount","type":"uint240"},{"internalType":"uint240","name":"maxAmount","type":"uint240"}],"name":"ExceedsMaxRepayAmount","type":"error"},{"inputs":[{"internalType":"uint40","name":"deadline","type":"uint40"}],"name":"ExpiredMintProposal","type":"error"},{"inputs":[],"name":"FrozenMinter","type":"error"},{"inputs":[],"name":"FutureTimestamp","type":"error"},{"inputs":[],"name":"InactiveMinter","type":"error"},{"inputs":[],"name":"InvalidMintProposal","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureLength","type":"error"},{"inputs":[],"name":"InvalidSignatureOrder","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidSignatureV","type":"error"},{"inputs":[],"name":"InvalidUInt112","type":"error"},{"inputs":[],"name":"InvalidUInt240","type":"error"},{"inputs":[],"name":"InvalidUInt48","type":"error"},{"inputs":[],"name":"NotApprovedMinter","type":"error"},{"inputs":[{"internalType":"address","name":"validator","type":"address"}],"name":"NotApprovedValidator","type":"error"},{"inputs":[{"internalType":"uint256","name":"validSignatures","type":"uint256"},{"internalType":"uint256","name":"requiredThreshold","type":"uint256"}],"name":"NotEnoughValidSignatures","type":"error"},{"inputs":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"lastSignatureTimestamp","type":"uint256"}],"name":"OutdatedValidatorTimestamp","type":"error"},{"inputs":[],"name":"OverflowsPrincipalOfTotalOwedM","type":"error"},{"inputs":[{"internalType":"uint40","name":"activeTimestamp","type":"uint40"}],"name":"PendingMintProposal","type":"error"},{"inputs":[{"internalType":"uint240","name":"totalPendingRetrievals","type":"uint240"},{"internalType":"uint240","name":"collateral","type":"uint240"}],"name":"RetrievalsExceedCollateral","type":"error"},{"inputs":[],"name":"SignatureArrayLengthsMismatch","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":[{"internalType":"uint40","name":"newTimestamp","type":"uint40"},{"internalType":"uint40","name":"earliestAllowedTimestamp","type":"uint40"}],"name":"StaleCollateralUpdate","type":"error"},{"inputs":[],"name":"StillApprovedMinter","type":"error"},{"inputs":[{"internalType":"uint256","name":"activeOwedM","type":"uint256"},{"internalType":"uint256","name":"maxAllowedOwedM","type":"uint256"}],"name":"Undercollateralized","type":"error"},{"inputs":[],"name":"ZeroBurnAmount","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroMintAmount","type":"error"},{"inputs":[],"name":"ZeroMintDestination","type":"error"},{"inputs":[],"name":"ZeroRetrievalAmount","type":"error"},{"inputs":[],"name":"ZeroTTGRegistrar","type":"error"},{"inputs":[],"name":"ZeroTTGVault","type":"error"},{"inputs":[],"name":"ZeroTimestamp","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"payer","type":"address"}],"name":"BurnExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint112","name":"principalAmount","type":"uint112"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"payer","type":"address"}],"name":"BurnExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"collateral","type":"uint240"},{"indexed":false,"internalType":"uint240","name":"totalResolvedCollateralRetrieval","type":"uint240"},{"indexed":true,"internalType":"bytes32","name":"metadataHash","type":"bytes32"},{"indexed":false,"internalType":"uint40","name":"timestamp","type":"uint40"}],"name":"CollateralUpdated","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":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"canceller","type":"address"}],"name":"MintCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint112","name":"principalAmount","type":"uint112"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"}],"name":"MintExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"destination","type":"address"}],"name":"MintProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MinterActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"inactiveOwedM","type":"uint240"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MinterDeactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint40","name":"frozenUntil","type":"uint40"}],"name":"MinterFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint40","name":"missedIntervals","type":"uint40"},{"indexed":false,"internalType":"uint240","name":"penaltyAmount","type":"uint240"}],"name":"MissedIntervalsPenaltyImposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"retrievalId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"}],"name":"RetrievalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"retrievalId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"RetrievalResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"excessOwedM","type":"uint240"},{"indexed":false,"internalType":"uint40","name":"timeSpan","type":"uint40"},{"indexed":false,"internalType":"uint240","name":"penaltyAmount","type":"uint240"}],"name":"UndercollateralizedPenaltyImposed","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINT_RATIO","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_UPDATE_COLLATERAL_INTERVAL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPDATE_COLLATERAL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"activateMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"activeOwedMOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"maxPrincipalAmount_","type":"uint256"},{"internalType":"uint256","name":"maxAmount_","type":"uint256"}],"name":"burnM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"maxAmount_","type":"uint256"}],"name":"burnM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"mintId_","type":"uint256"}],"name":"cancelMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralExpiryTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralPenaltyDeadlineOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralUpdateTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"deactivateMinter","outputs":[{"internalType":"uint240","name":"inactiveOwedM_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields_","type":"bytes1"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"version_","type":"string"},{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"verifyingContract_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"uint256[]","name":"extensions_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessOwedM","outputs":[{"internalType":"uint240","name":"excessOwedM_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"freezeMinter","outputs":[{"internalType":"uint40","name":"frozenUntil_","type":"uint40"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"frozenUntilOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"address","name":"validator_","type":"address"}],"name":"getLastSignatureTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"collateral_","type":"uint256"},{"internalType":"uint256[]","name":"retrievalIds_","type":"uint256[]"},{"internalType":"bytes32","name":"metadataHash_","type":"bytes32"},{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"getUpdateCollateralDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"inactiveOwedMOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isActiveMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isDeactivatedMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isFrozenMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isMinterApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"validator_","type":"address"}],"name":"isValidatorApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"latestProposedRetrievalTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestUpdateTimestamp","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"maxAllowedActiveOwedMOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintDelay","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintId_","type":"uint256"}],"name":"mintM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"mintProposalOf","outputs":[{"internalType":"uint48","name":"mintId_","type":"uint48"},{"internalType":"uint40","name":"createdAt_","type":"uint40"},{"internalType":"address","name":"destination_","type":"address"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatio","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintTTL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterFreezeTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterRate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"penalizedUntilOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"retrievalId_","type":"uint256"}],"name":"pendingCollateralRetrievalOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"principalOfActiveOwedMOf","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalOfTotalActiveOwedM","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"proposeMint","outputs":[{"internalType":"uint48","name":"mintId_","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"name":"proposeRetrieval","outputs":[{"internalType":"uint48","name":"retrievalId_","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rateModel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalActiveOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInactiveOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"totalPendingCollateralRetrievalOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ttgRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ttgVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"},{"internalType":"uint256[]","name":"retrievalIds_","type":"uint256[]"},{"internalType":"bytes32","name":"metadataHash_","type":"bytes32"},{"internalType":"address[]","name":"validators_","type":"address[]"},{"internalType":"uint256[]","name":"timestamps_","type":"uint256[]"},{"internalType":"bytes[]","name":"signatures_","type":"bytes[]"}],"name":"updateCollateral","outputs":[{"internalType":"uint40","name":"minTimestamp_","type":"uint40"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateCollateralInterval","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateCollateralValidatorThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateIndex","outputs":[{"internalType":"uint128","name":"index_","type":"uint128"}],"stateMutability":"nonpayable","type":"function"}]

61012060405234801562000011575f80fd5b506040516200583638038062005836833981016040819052620000349162000277565b60408051808201909152600d81526c4d696e7465724761746577617960981b60208201525f805464ffffffffff4216600160a01b027fffffffffffffff0000000000ffffffff000000000000000000000000000000009091161764e8d4a510001790556001620000a582826200034b565b5046608052620000b462000154565b60a052506001600160a01b03821660c0819052620000e55760405163b8eb034b60e01b815260040160405180910390fd5b5f620000f183620001f1565b6001600160a01b031660e0819052036200011e576040516331c1de8560e01b815260040160405180910390fd5b6001600160a01b0381166101008190526200014c5760405163b01d5e2b60e01b815260040160405180910390fd5b5050620004b4565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405162000189919062000417565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200022f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000255919062000491565b92915050565b80516001600160a01b038116811462000272575f80fd5b919050565b5f806040838503121562000289575f80fd5b62000294836200025b565b9150620002a4602084016200025b565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680620002d657607f821691505b602082108103620002f557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200034657805f5260205f20601f840160051c81016020851015620003225750805b601f840160051c820191505b8181101562000343575f81556001016200032e565b50505b505050565b81516001600160401b03811115620003675762000367620002ad565b6200037f81620003788454620002c1565b84620002fb565b602080601f831160018114620003b5575f84156200039d5750858301515b5f19600386901b1c1916600185901b1785556200040f565b5f85815260208120601f198616915b82811015620003e557888601518255948401946001909101908401620003c4565b50858210156200040357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f8083546200042681620002c1565b60018281168015620004415760018114620004575762000485565b60ff198416875282151583028701945062000485565b875f526020805f205f5b858110156200047c5781548a82015290840190820162000461565b50505082870194505b50929695505050505050565b5f60208284031215620004a2575f80fd5b620004ad826200025b565b9392505050565b60805160a05160c05160e051610100516152d4620005625f395f81816108d601528181610f34015281816119c601528181611fbf01528181612405015261246901525f818161070101526123b701525f81816107d301528181610b9201528181611344015281816113c3015281816113fd0152818161166701528181611b0501528181611ba701528181612324015281816127f30152612a3c01525f61139b01525f61136b01526152d45ff3fe608060405234801561000f575f80fd5b506004361061037d575f3560e01c8063a1088459116101d4578063c8a7d5bf11610109578063e62aa759116100a9578063f20eb87d11610079578063f20eb87d14610b48578063f5abed3214610b5b578063f7a31df614610b6e578063f962a44b14610b81575f80fd5b8063e62aa75914610a96578063e806250a14610ad3578063eda1599a14610b22578063f00c280c14610b35575f80fd5b8063d4084620116100e4578063d408462014610971578063d69527111461097a578063d6b7494f14610994578063e1ebf1ad1461099c575f80fd5b8063c8a7d5bf146108f8578063c8da88e11461093d578063cbf062f714610950575f80fd5b8063aab1d86f11610174578063b9f412b01161014f578063b9f412b01461085b578063c107634c14610863578063c2ee3a08146108b5578063c3b6f939146108d1575f80fd5b8063aab1d86f1461082d578063af9979c914610840578063b599105c14610853575f80fd5b8063a49c8461116101af578063a49c846114610773578063a59b9a35146107bb578063a6ce63cd146107ce578063a8c01961146107f5575f80fd5b8063a108845914610750578063a178094414610758578063a29b67ce1461076b575f80fd5b80634be1c1cd116102b557806374aaf5e91161025557806384b0196e1161022557806384b0196e146106d95780638fb7faf2146106f45780639675adb0146106fc57806399799bbd14610748575f80fd5b806374aaf5e9146106275780637572840e1461063a5780637ad636761461064d5780637efb685b1461069a575f80fd5b8063578f2aa011610290578063578f2aa0146105a7578063663485d7146105c25780636850a999146105cb57806371f8ffe5146105fd575f80fd5b80634be1c1cd146105595780635130406b1461056c57806353d96f2c1461057f575f80fd5b80633644e51511610320578063433ae061116102fb578063433ae061146104ee57806343dc2cad14610517578063452b9fd81461052a57806346f97d0b14610532575f80fd5b80633644e515146104ad5780633b547ae5146104c35780633f9bcc6c146104e6575f80fd5b806314bc32e81161035b57806314bc32e8146104105780631aefb1071461043a57806326987b601461047c57806334636e8e146104a5575f80fd5b80630ab18476146103815780630b88f09c146103a35780630ec06104146103b8575b5f80fd5b610389610b89565b60405163ffffffff90911681526020015b60405180910390f35b6103b66103b1366004614b56565b610bc0565b005b6103cb6103c6366004614b7e565b610ced565b604080516dffffffffffffffffffffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520161039a565b61042361041e366004614bae565b610fb3565b60405165ffffffffffff909116815260200161039a565b61044d610448366004614bd8565b611222565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b6104846112af565b6040516fffffffffffffffffffffffffffffffff909116815260200161039a565b61038961133b565b6104b5611368565b60405190815260200161039a565b6104d66104d1366004614bd8565b6113bd565b604051901515815260200161039a565b6103896113ee565b6105016104fc366004614c32565b611426565b60405164ffffffffff909116815260200161039a565b6103b6610525366004614bd8565b61154b565b61038961165e565b6104b57f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e81565b61044d610567366004614bd8565b61168b565b6104b561057a366004614d02565b61170b565b5f546105019074010000000000000000000000000000000000000000900464ffffffffff1681565b5f54610484906fffffffffffffffffffffffffffffffff1681565b610389610e1081565b6105de6105d9366004614bd8565b611725565b6040516dffffffffffffffffffffffffffff909116815260200161039a565b60025461044d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6104b5610635366004614bd8565b61179f565b610501610648366004614bd8565b611820565b61050161065b366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff1690565b6105016106a8366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015464ffffffffff1690565b6106e161186b565b60405161039a9796959493929190614dd4565b61044d6119a4565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b61044d6119c2565b610723611aff565b610501610766366004614bd8565b611b29565b610389611b9b565b610501610781366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015465010000000000900464ffffffffff1690565b6103cb6107c9366004614e94565b611bd3565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b6104d6610803366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090205460ff1690565b61044d61083b366004614bd8565b61202d565b61044d61084e366004614bd8565b6122a1565b6104b561231e565b610484612348565b610501610871366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546f01000000000000000000000000000000900464ffffffffff1690565b6108be61271081565b60405161ffff909116815260200161039a565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b6104b5610906366004614eab565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260086020908152604080832093909416825291909152205490565b61042361094b366004614e94565b6124f5565b5f54700100000000000000000000000000000000900463ffffffff16610389565b61038961fde881565b6003546105de906dffffffffffffffffffffffffffff1681565b6103896127ea565b610a2c6109aa366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff9081165f908152600560205260409020805460019091015465ffffffffffff82169364ffffffffff6601000000000000840416936b010000000000000000000000909304909216917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690565b6040805165ffffffffffff909516855264ffffffffff909316602085015273ffffffffffffffffffffffffffffffffffffffff909116918301919091527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606082015260800161039a565b6104d6610aa4366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f90815260046020526040902054610100900460ff1690565b6104d6610ae1366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421090565b61044d610b30366004614b56565b612817565b6103cb610b43366004614b56565b6128bc565b610501610b56366004614bd8565b6128f4565b61044d610b69366004614bd8565b6129b8565b6104d6610b7c366004614bd8565b612a36565b61044d612a61565b5f610bbb610bb67f0000000000000000000000000000000000000000000000000000000000000000612a94565b612abf565b905090565b610bc933612ace565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526005602052604090205465ffffffffffff168181141580610c0b575065ffffffffffff8116155b15610c42576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f8181526005602052604080822080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff0000000000000000000000000000000000000000000000000000000000001690555133929165ffffffffffff8516917f84fab121b74a9cdfebabf1215a1abbe5fe44ba6c1920780c593aa5102a4062369190a4505050565b5f80831580610cfa575082155b15610d31576040517f97b9d4c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260409020805460ff1680158015610d6f57508154610100900460ff16155b15610da6576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015610e5d57610db587612b28565b610dd087610dc288612ccd565b610dcb88612d1a565b612d73565b604080516dffffffffffffffffffffffffffff841681527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83166020820152929650909450339173ffffffffffffffffffffffffffffffffffffffff8a16917f48f6e7b6e5f16208a0eab9aa837aca65cc7ec94c304ed11ed98c2efc418ba50e910160405180910390a3610ee0565b610e6f87610e6a87612d1a565b612efb565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152909350339073ffffffffffffffffffffffffffffffffffffffff8916907f1391bf1af04eb2e8c6bd62f9c85f621584bc4ed7d2a6def75a0a0835756415429060200160405180910390a35b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac906044015f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b50505050610fa8612348565b505050935093915050565b5f33610fbe81612fa2565b610fc733613000565b835f03611000576040517f215b990c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831661104d576040517f785187dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61105785612d1a565b90506110633382613076565b600380547fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff81166e0100000000000000000000000000009182900465ffffffffffff908116600190810180831694850293909317909455604080516080810182528481524264ffffffffff908116602080840191825273ffffffffffffffffffffffffffffffffffffffff8d81168587018181527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8d811660608901818152335f818152600589528c90209a518b54995195519d167fffffffffffffffffffffffffffffffffffffffffff00000000000000000000009099169890981766010000000000009490991693909302979097177fff0000000000000000000000000000000000000000ffffffffffffffffffffff166b0100000000000000000000009a9094169990990292909217865590519490990180547fffff00000000000000000000000000000000000000000000000000000000000016949096169390931790945591519182529297509092917fc9b63ed98dd6e3f2536b8c9cd1668dd153c8a9207579556cb74ce9168b9c21d8910160405180910390a4505092915050565b5f61122c82611820565b64ffffffffff16421061124057505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600181015481547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182169162010000909104168082106112a757505f949350505050565b039392505050565b5f8054610bbb90611322906fffffffffffffffffffffffffffffffff81169061131d906112f590700100000000000000000000000000000000900463ffffffff16613144565b5f5474010000000000000000000000000000000000000000900464ffffffffff16420361315b565b61317d565b71ffffffffffffffffffffffffffffffffffff166131b1565b5f610bbb610bb67f00000000000000000000000000000000000000000000000000000000000000006131cc565b5f7f0000000000000000000000000000000000000000000000000000000000000000461461139857610bbb6131f7565b507f000000000000000000000000000000000000000000000000000000000000000090565b5f6113e87f000000000000000000000000000000000000000000000000000000000000000083613292565b92915050565b5f610bbb61fde8611421610bb67f00000000000000000000000000000000000000000000000000000000000000006132be565b6132e9565b5f3361143181612fa2565b86831415806114405750828514155b15611477576040517fa68dc7d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61148a338d8d8d8d8d8d8d8d8d8d61330a565b91505f6114968d612d1a565b90505f6114a4338e8e6134e1565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85811682528316602082015264ffffffffff87168183015290519192508c9133917f8c7a373ea6d1cedfcb77f0e5520921cc5d5a1a16b960c0c13c0f96b8dc24caa8919081900360600190a361151c33612b28565b611526338561368c565b6115313383866138a8565b611539612348565b505050509a9950505050505050505050565b61155481612a36565b61158a576040517f70b34fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090208054610100900460ff16156115ef576040517f30b2dfb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155604051339073ffffffffffffffffffffffffffffffffffffffff8416907f2531adeb71f8681e6f4644f88cb102c71370151986071c92d43a7e82d217462a905f90a35050565b5f610bbb610bb67f00000000000000000000000000000000000000000000000000000000000000006139ea565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166116bd575f6113e8565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020546113e8907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613a15565b5f61171a878787878787613a27565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff16611757575f6113e8565b5073ffffffffffffffffffffffffffffffffffffffff165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166117d1575f6113e8565b6127106117dc6113ee565b63ffffffff166117eb84611222565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16028161181957611819614ed3565b0492915050565b5f611829611b9b565b73ffffffffffffffffffffffffffffffffffffffff9092165f9081526004602052604090206002015464ffffffffff1663ffffffff9290921691909101919050565b5f606080828080836001463084806040519080825280602002602001820160405280156118a2578160200160208202803683370190505b507f0f0000000000000000000000000000000000000000000000000000000000000094939291908480546118d590614f00565b80601f016020809104026020016040519081016040528092919081815260200182805461190190614f00565b801561194c5780601f106119235761010080835404028352916020019161194c565b820191905f5260205f20905b81548152906001019060200180831161192f57829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6003545f90610bbb906dffffffffffffffffffffffffffff16613a15565b5f807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a519190614f51565b6002546003549192505f917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690611aa0906dffffffffffffffffffffffffffff16611a9b6112af565b613af8565b611aaa9190614f95565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115611afa5703919050565b505090565b5f610bbb7f0000000000000000000000000000000000000000000000000000000000000000613b03565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812081611b56611b9b565b60028301549091505f90611b7e9064ffffffffff808216916501000000000090041684613b34565b9150611b92905063ffffffff831682614fd3565b95945050505050565b5f610bbb611bcb610bb67f0000000000000000000000000000000000000000000000000000000000000000613bbd565b610e10613be8565b5f8033611bdf81612fa2565b611be833613000565b335f908152600560205260409020805460018201547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16935065ffffffffffff81169064ffffffffff6601000000000000820416906b010000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16878314611c99576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611ca261133b565b63ffffffff16830190508064ffffffffff16421015611cfb576040517fdddc549900000000000000000000000000000000000000000000000000000000815264ffffffffff821660048201526024015b60405180910390fd5b5f611d04610b89565b63ffffffff16820190508064ffffffffff16421115611d58576040517fa170e12000000000000000000000000000000000000000000000000000000000815264ffffffffff82166004820152602401611cf2565b5050611d643387613076565b335f90815260056020526040902080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff000000000000000000000000000000000000000000000000000000000000169055611dca86613c02565b600354604080516dffffffffffffffffffffffffffff80851682527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b166020830152939a509290911691339165ffffffffffff8716917fc7f1a91b0a382c263a18fea7b2908a6fcd90ebf2a9fde4bdf483b8ecceff1311910160405180910390a36002546dffffffffffffffffffffffffffff828116818b160191611e8d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613c02565b6dffffffffffffffffffffffffffff16820110611ed6576040517f1123990900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff928316179055335f908152600660205260409081902080547fffff0000000000000000000000000000000000000000000000000000000000008116938c167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216018116939093179055517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015291891660248201527f000000000000000000000000000000000000000000000000000000000000000091909116906340c10f19906044015f604051808303815f87803b158015612003575f80fd5b505af1158015612015573d5f803e3d5ffd5b50505050612021612348565b50505050505050915091565b5f8161203881612fa2565b61204183612a36565b15612078576040517fe2a4b46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61208183612b28565b5f61208b84611725565b905061209681613a15565b600380547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff918216859003909116179055600280547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91821684018216179091556040519082168152909350339073ffffffffffffffffffffffffffffffffffffffff8616907f441d4e9d05bbbeb9019e911d76bdd0bf9389b0755175567d4e59607be5348d9b9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff84165f908152600460209081526040808320838155600180820180547fffff0000000000000000000000000000000000000000000000000000000000009081169091556002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556005855283862080547fff0000000000000000000000000000000000000000000000000000000000000016815590910180548216905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017909155600690925290912080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86169216919091179055612299612348565b505050919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f92915050565b5f610bbb7f0000000000000000000000000000000000000000000000000000000000000000613c14565b5f806123526119c2565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81161561245d576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660248301527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044015f604051808303815f87803b158015612446575f80fd5b505af1158015612458573d5f803e3d5ffd5b505050505b612465613c3f565b91507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b9f412b06040518163ffffffff1660e01b81526004016020604051808303815f875af11580156124d1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa9190614ff1565b5f3361250081612fa2565b825f03612539576040517faa58a19400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff8116740100000000000000000000000000000000000000009182900465ffffffffffff90811660010190811690920217909155335f9081526004602052604081208054929450916201000090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16906125d586612d1a565b60018401549091505f9061260a9083907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16614f95565b9050807dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1610156126b0576040517f10bb3f430000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b60028401805464ffffffffff42166f01000000000000000000000000000000027fffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffff9091161790556001840180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084167fffff00000000000000000000000000000000000000000000000000000000000092831617909255335f81815260076020908152604080832065ffffffffffff8d16845290915281208054948716949093169390931790915561278191613076565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83168152339065ffffffffffff8816907f83f709bc37ea6de507e30b18311aa7a86c08833fa447f2c76ccc463c8936c4999060200160405180910390a35050505050919050565b5f610bbb610bb67f0000000000000000000000000000000000000000000000000000000000000000613d8f565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040812054610100900460ff166128b35773ffffffffffffffffffffffffffffffffffffffff83165f9081526007602052604081209061287684613dba565b65ffffffffffff16815260208101919091526040015f20547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166128b5565b5f5b9392505050565b5f806128e9846128d36128ce86612d1a565b613dfb565b6dffffffffffffffffffffffffffff1685610ced565b909590945092505050565b5f6128fe33612ace565b61290661165e565b73ffffffffffffffffffffffffffffffffffffffff83165f8181526004602090815260409182902060020180547fffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffff166a010000000000000000000063ffffffff96909616420164ffffffffff811696870291909117909155915193845290935090917f1ac7b35bca40be2823e4880f1b2e9ef02fa4c7bd62aed73c2ea2959232a1f9f5910160405180910390a2919050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812054610100900460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f6113e87f000000000000000000000000000000000000000000000000000000000000000083613e0d565b6002545f907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16612a8e6119a4565b01905090565b5f6113e8827f6d696e745f74746c000000000000000000000000000000000000000000000000613e39565b5f6113e88263ffffffff613ec8565b612ad7816113bd565b612b25576040517ff731555300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401611cf2565b50565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812060028101549091908190612b7b9064ffffffffff8082169165010000000000900416612b76611b9b565b613b34565b915091508164ffffffffff165f03612b935750505050565b6002830180547fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000064ffffffffff8416021790555f612bd885611725565b9050806dffffffffffffffffffffffffffff165f03612bf8575050505050565b5f612c2386612c1e64ffffffffff87166dffffffffffffffffffffffffffff8616615020565b613ed6565b9050806dffffffffffffffffffffffffffff165f03612c4457505050505050565b8573ffffffffffffffffffffffffffffffffffffffff167f4fb8bb7b0278c9f68d34ce501b521ecd2e3c1bc48fe37eda47db36da6defc7ef85612c8684613a15565b6040805164ffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a2505050505050565b5f6dffffffffffffffffffffffffffff821115612d16576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612d16576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80612d87612d8186611725565b85614036565b9150612d9282613a15565b9050827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115612e38576040517f37666f1d0000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b73ffffffffffffffffffffffffffffffffffffffff949094165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081166dffffffffffffffffffffffffffff8481167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384160390921617909155600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831684900390921691909117905593915050565b5f612f0e612f08846122a1565b83614064565b73ffffffffffffffffffffffffffffffffffffffff9093165f90815260066020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821686900381167fffff0000000000000000000000000000000000000000000000000000000000009283161790925560028054808416879003909316929091169190911790555090919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090205460ff16612b25576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421015612b25576040517f1526e62e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6130808361179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106130ac57505050565b5f827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166130d78561168b565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160190508181111561313e576040517f5033ec120000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401611cf2565b50505050565b5f61271063ffffffff831664e8d4a5100002611819565b5f6128b56301e1338067ffffffffffffffff851663ffffffff851602046140b2565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff84160264e8d4a50fff015b049392505050565b5f6113e8826fffffffffffffffffffffffffffffffff613ec8565b5f6113e8827f6d696e745f64656c617900000000000000000000000000000000000000000000613e39565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161322a919061505e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6128b5837f76616c696461746f7273000000000000000000000000000000000000000000008461412b565b5f6113e8827f6d696e745f726174696f00000000000000000000000000000000000000000000613e39565b5f8163ffffffff168363ffffffff161061330357816128b5565b5090919050565b425f805b83811015613480575f811180156133a0575088886001830381811061333557613335615133565b905060200201602081019061334a9190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1689898381811061337257613372615133565b90506020020160208101906133879190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1611155b156133d7576040517f139f3c9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134488e8e8e8e8e8e8e888181106133f1576133f1615133565b90506020020160208101906134069190614bd8565b8d8d8981811061341857613418615133565b905060200201358c8c8a81811061343157613431615133565b90506020028101906134439190615160565b6141cb565b156134785761346f8388888481811061346357613463615133565b9050602002013561438d565b92508160010191505b60010161330e565b505f61348a61231e565b9050808210156134d0576040517f74e8306f0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611cf2565b50509b9a5050505050505050505050565b5f805b8281101561360d575f61350e85858481811061350257613502615133565b90506020020135613dba565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260076020908152604080832065ffffffffffff851684529091528120549192507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091169081900361357a575050613605565b73ffffffffffffffffffffffffffffffffffffffff87165f81815260076020908152604080832065ffffffffffff8716808552925280832080547fffff000000000000000000000000000000000000000000000000000000000000169055519684019690917f7318b8ec2c2570ee6068ac690bbda62f9d13d38d52e36e23e0885101a5ffab0791a350505b6001016134e4565b5073ffffffffffffffffffffffffffffffffffffffff9093165f90815260046020526040902060010180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808216869003167fffff0000000000000000000000000000000000000000000000000000000000009091161790555090919050565b5f61369683611725565b9050806dffffffffffffffffffffffffffff165f036136b457505050565b5f6136be8461179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106136eb5750505050565b5f6136f582613dfb565b9050826dffffffffffffffffffffffffffff16816dffffffffffffffffffffffffffff1610613725575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260408120600281015490919061376e9064ffffffffff80821691650100000000009004166143a9565b90508064ffffffffff168664ffffffffff161161378e5750505050505050565b8086038386035f6137e48a6137a1611b9b565b63ffffffff168564ffffffffff1685026dffffffffffffffffffffffffffff16816137ce576137ce614ed3565b046dffffffffffffffffffffffffffff16613ed6565b9050806dffffffffffffffffffffffffffff165f036138095750505050505050505050565b8973ffffffffffffffffffffffffffffffffffffffff167fadbe0a005ef4d2903a69fd3053b1c8dfc083430632f549a92a1d3bc648104ae361384a84613a15565b8561385485613a15565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948516815264ffffffffff93909316602084015292168183015290519081900360600190a250505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600460205260408120600281015490919061391d906138ff9064ffffffffff808216916f010000000000000000000000000000009004166143a9565b61391661390a611b9b565b63ffffffff164261438d565b42036143a9565b90508064ffffffffff168364ffffffffff1611613977576040517f17ea884500000000000000000000000000000000000000000000000000000000815264ffffffffff808516600483015282166024820152604401611cf2565b5080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909316620100000261ffff9093169290921782556002909101805464ffffffffff9092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090921691909117905550565b5f6113e8827f6d696e7465725f667265657a655f74696d650000000000000000000000000000613e39565b5f6113e882613a226112af565b6143c5565b5f61171a7f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e5f1b88888888604051602001613a639291906151c1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083019590955273ffffffffffffffffffffffffffffffffffffffff909316928101929092526060820152608081019190915260a0810185905260c0810184905260e001604051602081830303815290604052805190602001206143d0565b5f6128b58383614431565b5f6113e8613b31837f6d696e7465725f726174655f6d6f64656c000000000000000000000000000000613e39565b90565b5f805f613b4186866143a9565b90508564ffffffffff165f03613b5b575f92509050613bb5565b5f613b668242615201565b90508463ffffffff168164ffffffffff161015613b8857505f92509050613bb5565b8463ffffffff168164ffffffffff1681613ba457613ba4614ed3565b0493505063ffffffff841683020190505b935093915050565b5f6113e8827f7570646174655f636f6c6c61746572616c5f696e74657276616c000000000000613e39565b5f8163ffffffff168363ffffffff161161330357816128b5565b5f6113e882613c0f6112af565b614462565b5f6113e8827f7570646174655f636f6c6c61746572616c5f7468726573686f6c640000000000613e39565b5f80613c4961446d565b5f5490915064ffffffffff740100000000000000000000000000000000000000009091041642148015613c9957505f5463ffffffff82811670010000000000000000000000000000000090920416145b15613cb75750505f546fffffffffffffffffffffffffffffffff1690565b613cbf6112af565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b5f6113e8827f70656e616c74795f726174650000000000000000000000000000000000000000613e39565b5f65ffffffffffff821115612d16576040517f069c6d4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6113e882613e086112af565b61456d565b5f6128b5837f6d696e74657273000000000000000000000000000000000000000000000000008461412b565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015613ea4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128b59190614f51565b5f81831061330357816128b5565b5f8172ffffffffffffffffffffffffffffffffffffff165f03613efa57505f6113e8565b5f613f036127ea565b90508063ffffffff165f03613f1b575f9150506113e8565b60035461271063ffffffff831672ffffffffffffffffffffffffffffffffffffff86160204906dffffffffffffffffffffffffffff908116820190811115613f7a5750506003546dffffffffffffffffffffffffffff90811681038116905b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff92831617905573ffffffffffffffffffffffffffffffffffffffff959095165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081169683167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216011695909517909455509192915050565b5f816dffffffffffffffffffffffffffff16836dffffffffffffffffffffffffffff161061330357816128b5565b5f817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161061330357816128b5565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161412157614121614ed3565b0495945050505050565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa15801561419f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141c3919061521f565b949350505050565b5f835f03614205576040517fda16d76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4264ffffffffff16841115614246576040517f0ff02cef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808b165f908152600860209081526040808320938916835292905220548085116142d7576040517f2e7a0bcb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024810186905260448101829052606401611cf2565b6142e0866113bd565b6142ed575f915050614380565b61433a866142ff8d8d8d8d8d8c613a27565b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061457892505050565b614347575f915050614380565b505073ffffffffffffffffffffffffffffffffffffffff808a165f90815260086020908152604080832093881683529290522083905560015b9998505050505050505050565b5f8164ffffffffff168364ffffffffff161061330357816128b5565b5f8164ffffffffff168364ffffffffff161161330357816128b5565b5f6128b58383614595565b5f6143d9611368565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026131a9565b5f6128b583836145cd565b5f805f614478611aff565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916144f4919061523e565b5f60405180830381855afa9150503d805f811461452c576040519150601f19603f3d011682016040523d82523d5f602084013e614531565b606091505b509150915081801561454557506020815110155b61454f575f614566565b61456681806020019051810190610bb69190614f51565b9250505090565b5f6128b5838361468e565b5f614584848484614725565b806141c357506141c3848484614774565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff84160264e8d4a50fff016131a9565b5f816fffffffffffffffffffffffffffffffff165f03614619576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161468857614688614ed3565b04612ccd565b5f816fffffffffffffffffffffffffffffffff165f036146da576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161468857614688614ed3565b5f815160400361474e5760208201516040830151614745868684846148bd565b925050506128b5565b5f61475a8585856148e6565b600581111561476b5761476b615259565b14949350505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff1685856040516024016147a1929190615286565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251614822919061523e565b5f60405180830381855afa9150503d805f811461485a576040519150601f19603f3d011682016040523d82523d5f602084013e61485f565b606091505b509150915081801561487357506020815110155b80156148b3575080517f1626ba7e00000000000000000000000000000000000000000000000000000000906148b19083016020908101908401614f51565b145b9695505050505050565b5f806148cb86868686614920565b60058111156148dc576148dc615259565b1495945050505050565b5f805f6148f3858561495b565b90925090505f82600581111561490b5761490b615259565b1461491657816148b3565b6148b3868261499f565b5f805f61492e8686866149da565b90925090505f82600581111561494657614946615259565b14614951578161171a565b61171a878261499f565b5f8082516041146149715750600290505f614998565b6020830151604084015160608501515f1a919061499087848484614a21565b945094505050505b9250929050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146128b35760056128b5565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416614a1387838884614a21565b935093505050935093915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614a565750600390505f614b25565b8460ff16601b14158015614a6e57508460ff16601c14155b15614a7e5750600490505f614b25565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614ace573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615614b1c575f81614b20565b60015f5b915091505b94509492505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614b51575f80fd5b919050565b5f8060408385031215614b67575f80fd5b614b7083614b2e565b946020939093013593505050565b5f805f60608486031215614b90575f80fd5b614b9984614b2e565b95602085013595506040909401359392505050565b5f8060408385031215614bbf575f80fd5b82359150614bcf60208401614b2e565b90509250929050565b5f60208284031215614be8575f80fd5b6128b582614b2e565b5f8083601f840112614c01575f80fd5b50813567ffffffffffffffff811115614c18575f80fd5b6020830191508360208260051b8501011115614998575f80fd5b5f805f805f805f805f8060c08b8d031215614c4b575f80fd5b8a35995060208b013567ffffffffffffffff80821115614c69575f80fd5b614c758e838f01614bf1565b909b50995060408d0135985060608d0135915080821115614c94575f80fd5b614ca08e838f01614bf1565b909850965060808d0135915080821115614cb8575f80fd5b614cc48e838f01614bf1565b909650945060a08d0135915080821115614cdc575f80fd5b50614ce98d828e01614bf1565b915080935050809150509295989b9194979a5092959850565b5f805f805f8060a08789031215614d17575f80fd5b614d2087614b2e565b955060208701359450604087013567ffffffffffffffff811115614d42575f80fd5b614d4e89828a01614bf1565b979a9699509760608101359660809091013595509350505050565b5f5b83811015614d83578181015183820152602001614d6b565b50505f910152565b5f8151808452614da2816020860160208601614d69565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e06020840152614e1060e084018a614d8b565b8381036040850152614e22818a614d8b565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b81811015614e8257835183529284019291840191600101614e66565b50909c9b505050505050505050505050565b5f60208284031215614ea4575f80fd5b5035919050565b5f8060408385031215614ebc575f80fd5b614ec583614b2e565b9150614bcf60208401614b2e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b600181811c90821680614f1457607f821691505b602082108103614f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215614f61575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115614fcc57614fcc614f68565b5092915050565b64ffffffffff818116838216019080821115614fcc57614fcc614f68565b5f60208284031215615001575f80fd5b81516fffffffffffffffffffffffffffffffff811681146128b5575f80fd5b72ffffffffffffffffffffffffffffffffffffff82811682821681810283169291811582850482141761505557615055614f68565b50505092915050565b5f8083545f60018260011c9150600183168061507b57607f831692505b602080841082036150b3577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b8180156150c757600181146150fa57615125565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615125565b5f8a8152602090205f5b8681101561511d5781548b820152908501908301615104565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615193575f80fd5b83018035915067ffffffffffffffff8211156151ad575f80fd5b602001915036819003821315614998575f80fd5b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151ee575f80fd5b8260051b80858437919091019392505050565b64ffffffffff828116828216039080821115614fcc57614fcc614f68565b5f6020828403121561522f575f80fd5b815180151581146128b5575f80fd5b5f825161524f818460208701614d69565b9190910192915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b828152604060208201525f6141c36040830184614d8b56fea26469706673582212202fb093d96f0c6cdac2540fc9fd6bdc6dd1d2f7ff81d14968d63024c63999cf4364736f6c63430008170033000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061037d575f3560e01c8063a1088459116101d4578063c8a7d5bf11610109578063e62aa759116100a9578063f20eb87d11610079578063f20eb87d14610b48578063f5abed3214610b5b578063f7a31df614610b6e578063f962a44b14610b81575f80fd5b8063e62aa75914610a96578063e806250a14610ad3578063eda1599a14610b22578063f00c280c14610b35575f80fd5b8063d4084620116100e4578063d408462014610971578063d69527111461097a578063d6b7494f14610994578063e1ebf1ad1461099c575f80fd5b8063c8a7d5bf146108f8578063c8da88e11461093d578063cbf062f714610950575f80fd5b8063aab1d86f11610174578063b9f412b01161014f578063b9f412b01461085b578063c107634c14610863578063c2ee3a08146108b5578063c3b6f939146108d1575f80fd5b8063aab1d86f1461082d578063af9979c914610840578063b599105c14610853575f80fd5b8063a49c8461116101af578063a49c846114610773578063a59b9a35146107bb578063a6ce63cd146107ce578063a8c01961146107f5575f80fd5b8063a108845914610750578063a178094414610758578063a29b67ce1461076b575f80fd5b80634be1c1cd116102b557806374aaf5e91161025557806384b0196e1161022557806384b0196e146106d95780638fb7faf2146106f45780639675adb0146106fc57806399799bbd14610748575f80fd5b806374aaf5e9146106275780637572840e1461063a5780637ad636761461064d5780637efb685b1461069a575f80fd5b8063578f2aa011610290578063578f2aa0146105a7578063663485d7146105c25780636850a999146105cb57806371f8ffe5146105fd575f80fd5b80634be1c1cd146105595780635130406b1461056c57806353d96f2c1461057f575f80fd5b80633644e51511610320578063433ae061116102fb578063433ae061146104ee57806343dc2cad14610517578063452b9fd81461052a57806346f97d0b14610532575f80fd5b80633644e515146104ad5780633b547ae5146104c35780633f9bcc6c146104e6575f80fd5b806314bc32e81161035b57806314bc32e8146104105780631aefb1071461043a57806326987b601461047c57806334636e8e146104a5575f80fd5b80630ab18476146103815780630b88f09c146103a35780630ec06104146103b8575b5f80fd5b610389610b89565b60405163ffffffff90911681526020015b60405180910390f35b6103b66103b1366004614b56565b610bc0565b005b6103cb6103c6366004614b7e565b610ced565b604080516dffffffffffffffffffffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520161039a565b61042361041e366004614bae565b610fb3565b60405165ffffffffffff909116815260200161039a565b61044d610448366004614bd8565b611222565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b6104846112af565b6040516fffffffffffffffffffffffffffffffff909116815260200161039a565b61038961133b565b6104b5611368565b60405190815260200161039a565b6104d66104d1366004614bd8565b6113bd565b604051901515815260200161039a565b6103896113ee565b6105016104fc366004614c32565b611426565b60405164ffffffffff909116815260200161039a565b6103b6610525366004614bd8565b61154b565b61038961165e565b6104b57f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e81565b61044d610567366004614bd8565b61168b565b6104b561057a366004614d02565b61170b565b5f546105019074010000000000000000000000000000000000000000900464ffffffffff1681565b5f54610484906fffffffffffffffffffffffffffffffff1681565b610389610e1081565b6105de6105d9366004614bd8565b611725565b6040516dffffffffffffffffffffffffffff909116815260200161039a565b60025461044d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6104b5610635366004614bd8565b61179f565b610501610648366004614bd8565b611820565b61050161065b366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff1690565b6105016106a8366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015464ffffffffff1690565b6106e161186b565b60405161039a9796959493929190614dd4565b61044d6119a4565b6107237f0000000000000000000000003dc71be52d6d687e21fc0d4ffc196f32cacbc26d81565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b61044d6119c2565b610723611aff565b610501610766366004614bd8565b611b29565b610389611b9b565b610501610781366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015465010000000000900464ffffffffff1690565b6103cb6107c9366004614e94565b611bd3565b6107237f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a81565b6104d6610803366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090205460ff1690565b61044d61083b366004614bd8565b61202d565b61044d61084e366004614bd8565b6122a1565b6104b561231e565b610484612348565b610501610871366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546f01000000000000000000000000000000900464ffffffffff1690565b6108be61271081565b60405161ffff909116815260200161039a565b6107237f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec081565b6104b5610906366004614eab565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260086020908152604080832093909416825291909152205490565b61042361094b366004614e94565b6124f5565b5f54700100000000000000000000000000000000900463ffffffff16610389565b61038961fde881565b6003546105de906dffffffffffffffffffffffffffff1681565b6103896127ea565b610a2c6109aa366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff9081165f908152600560205260409020805460019091015465ffffffffffff82169364ffffffffff6601000000000000840416936b010000000000000000000000909304909216917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690565b6040805165ffffffffffff909516855264ffffffffff909316602085015273ffffffffffffffffffffffffffffffffffffffff909116918301919091527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606082015260800161039a565b6104d6610aa4366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f90815260046020526040902054610100900460ff1690565b6104d6610ae1366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421090565b61044d610b30366004614b56565b612817565b6103cb610b43366004614b56565b6128bc565b610501610b56366004614bd8565b6128f4565b61044d610b69366004614bd8565b6129b8565b6104d6610b7c366004614bd8565b612a36565b61044d612a61565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a612a94565b612abf565b905090565b610bc933612ace565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526005602052604090205465ffffffffffff168181141580610c0b575065ffffffffffff8116155b15610c42576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f8181526005602052604080822080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff0000000000000000000000000000000000000000000000000000000000001690555133929165ffffffffffff8516917f84fab121b74a9cdfebabf1215a1abbe5fe44ba6c1920780c593aa5102a4062369190a4505050565b5f80831580610cfa575082155b15610d31576040517f97b9d4c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260409020805460ff1680158015610d6f57508154610100900460ff16155b15610da6576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015610e5d57610db587612b28565b610dd087610dc288612ccd565b610dcb88612d1a565b612d73565b604080516dffffffffffffffffffffffffffff841681527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83166020820152929650909450339173ffffffffffffffffffffffffffffffffffffffff8a16917f48f6e7b6e5f16208a0eab9aa837aca65cc7ec94c304ed11ed98c2efc418ba50e910160405180910390a3610ee0565b610e6f87610e6a87612d1a565b612efb565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152909350339073ffffffffffffffffffffffffffffffffffffffff8916907f1391bf1af04eb2e8c6bd62f9c85f621584bc4ed7d2a6def75a0a0835756415429060200160405180910390a35b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660248201527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac906044015f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b50505050610fa8612348565b505050935093915050565b5f33610fbe81612fa2565b610fc733613000565b835f03611000576040517f215b990c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831661104d576040517f785187dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61105785612d1a565b90506110633382613076565b600380547fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff81166e0100000000000000000000000000009182900465ffffffffffff908116600190810180831694850293909317909455604080516080810182528481524264ffffffffff908116602080840191825273ffffffffffffffffffffffffffffffffffffffff8d81168587018181527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8d811660608901818152335f818152600589528c90209a518b54995195519d167fffffffffffffffffffffffffffffffffffffffffff00000000000000000000009099169890981766010000000000009490991693909302979097177fff0000000000000000000000000000000000000000ffffffffffffffffffffff166b0100000000000000000000009a9094169990990292909217865590519490990180547fffff00000000000000000000000000000000000000000000000000000000000016949096169390931790945591519182529297509092917fc9b63ed98dd6e3f2536b8c9cd1668dd153c8a9207579556cb74ce9168b9c21d8910160405180910390a4505092915050565b5f61122c82611820565b64ffffffffff16421061124057505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600181015481547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182169162010000909104168082106112a757505f949350505050565b039392505050565b5f8054610bbb90611322906fffffffffffffffffffffffffffffffff81169061131d906112f590700100000000000000000000000000000000900463ffffffff16613144565b5f5474010000000000000000000000000000000000000000900464ffffffffff16420361315b565b61317d565b71ffffffffffffffffffffffffffffffffffff166131b1565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6131cc565b5f7f0000000000000000000000000000000000000000000000000000000000aa36a7461461139857610bbb6131f7565b507fe56392b5ec2f838ae0b0f0dd0ee7ca621a9431614779af9e8fa59e96b9cb40f090565b5f6113e87f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a83613292565b92915050565b5f610bbb61fde8611421610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6132be565b6132e9565b5f3361143181612fa2565b86831415806114405750828514155b15611477576040517fa68dc7d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61148a338d8d8d8d8d8d8d8d8d8d61330a565b91505f6114968d612d1a565b90505f6114a4338e8e6134e1565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85811682528316602082015264ffffffffff87168183015290519192508c9133917f8c7a373ea6d1cedfcb77f0e5520921cc5d5a1a16b960c0c13c0f96b8dc24caa8919081900360600190a361151c33612b28565b611526338561368c565b6115313383866138a8565b611539612348565b505050509a9950505050505050505050565b61155481612a36565b61158a576040517f70b34fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090208054610100900460ff16156115ef576040517f30b2dfb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155604051339073ffffffffffffffffffffffffffffffffffffffff8416907f2531adeb71f8681e6f4644f88cb102c71370151986071c92d43a7e82d217462a905f90a35050565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6139ea565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166116bd575f6113e8565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020546113e8907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613a15565b5f61171a878787878787613a27565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff16611757575f6113e8565b5073ffffffffffffffffffffffffffffffffffffffff165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166117d1575f6113e8565b6127106117dc6113ee565b63ffffffff166117eb84611222565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16028161181957611819614ed3565b0492915050565b5f611829611b9b565b73ffffffffffffffffffffffffffffffffffffffff9092165f9081526004602052604090206002015464ffffffffff1663ffffffff9290921691909101919050565b5f606080828080836001463084806040519080825280602002602001820160405280156118a2578160200160208202803683370190505b507f0f0000000000000000000000000000000000000000000000000000000000000094939291908480546118d590614f00565b80601f016020809104026020016040519081016040528092919081815260200182805461190190614f00565b801561194c5780601f106119235761010080835404028352916020019161194c565b820191905f5260205f20905b81548152906001019060200180831161192f57829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6003545f90610bbb906dffffffffffffffffffffffffffff16613a15565b5f807f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a519190614f51565b6002546003549192505f917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690611aa0906dffffffffffffffffffffffffffff16611a9b6112af565b613af8565b611aaa9190614f95565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115611afa5703919050565b505090565b5f610bbb7f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613b03565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812081611b56611b9b565b60028301549091505f90611b7e9064ffffffffff808216916501000000000090041684613b34565b9150611b92905063ffffffff831682614fd3565b95945050505050565b5f610bbb611bcb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613bbd565b610e10613be8565b5f8033611bdf81612fa2565b611be833613000565b335f908152600560205260409020805460018201547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16935065ffffffffffff81169064ffffffffff6601000000000000820416906b010000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16878314611c99576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611ca261133b565b63ffffffff16830190508064ffffffffff16421015611cfb576040517fdddc549900000000000000000000000000000000000000000000000000000000815264ffffffffff821660048201526024015b60405180910390fd5b5f611d04610b89565b63ffffffff16820190508064ffffffffff16421115611d58576040517fa170e12000000000000000000000000000000000000000000000000000000000815264ffffffffff82166004820152602401611cf2565b5050611d643387613076565b335f90815260056020526040902080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff000000000000000000000000000000000000000000000000000000000000169055611dca86613c02565b600354604080516dffffffffffffffffffffffffffff80851682527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b166020830152939a509290911691339165ffffffffffff8716917fc7f1a91b0a382c263a18fea7b2908a6fcd90ebf2a9fde4bdf483b8ecceff1311910160405180910390a36002546dffffffffffffffffffffffffffff828116818b160191611e8d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613c02565b6dffffffffffffffffffffffffffff16820110611ed6576040517f1123990900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff928316179055335f908152600660205260409081902080547fffff0000000000000000000000000000000000000000000000000000000000008116938c167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216018116939093179055517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015291891660248201527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec091909116906340c10f19906044015f604051808303815f87803b158015612003575f80fd5b505af1158015612015573d5f803e3d5ffd5b50505050612021612348565b50505050505050915091565b5f8161203881612fa2565b61204183612a36565b15612078576040517fe2a4b46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61208183612b28565b5f61208b84611725565b905061209681613a15565b600380547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff918216859003909116179055600280547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91821684018216179091556040519082168152909350339073ffffffffffffffffffffffffffffffffffffffff8616907f441d4e9d05bbbeb9019e911d76bdd0bf9389b0755175567d4e59607be5348d9b9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff84165f908152600460209081526040808320838155600180820180547fffff0000000000000000000000000000000000000000000000000000000000009081169091556002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556005855283862080547fff0000000000000000000000000000000000000000000000000000000000000016815590910180548216905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017909155600690925290912080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86169216919091179055612299612348565b505050919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f92915050565b5f610bbb7f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613c14565b5f806123526119c2565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81161561245d576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003dc71be52d6d687e21fc0d4ffc196f32cacbc26d811660048301527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660248301527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec016906340c10f19906044015f604051808303815f87803b158015612446575f80fd5b505af1158015612458573d5f803e3d5ffd5b505050505b612465613c3f565b91507f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff1663b9f412b06040518163ffffffff1660e01b81526004016020604051808303815f875af11580156124d1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa9190614ff1565b5f3361250081612fa2565b825f03612539576040517faa58a19400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff8116740100000000000000000000000000000000000000009182900465ffffffffffff90811660010190811690920217909155335f9081526004602052604081208054929450916201000090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16906125d586612d1a565b60018401549091505f9061260a9083907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16614f95565b9050807dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1610156126b0576040517f10bb3f430000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b60028401805464ffffffffff42166f01000000000000000000000000000000027fffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffff9091161790556001840180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084167fffff00000000000000000000000000000000000000000000000000000000000092831617909255335f81815260076020908152604080832065ffffffffffff8d16845290915281208054948716949093169390931790915561278191613076565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83168152339065ffffffffffff8816907f83f709bc37ea6de507e30b18311aa7a86c08833fa447f2c76ccc463c8936c4999060200160405180910390a35050505050919050565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613d8f565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040812054610100900460ff166128b35773ffffffffffffffffffffffffffffffffffffffff83165f9081526007602052604081209061287684613dba565b65ffffffffffff16815260208101919091526040015f20547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166128b5565b5f5b9392505050565b5f806128e9846128d36128ce86612d1a565b613dfb565b6dffffffffffffffffffffffffffff1685610ced565b909590945092505050565b5f6128fe33612ace565b61290661165e565b73ffffffffffffffffffffffffffffffffffffffff83165f8181526004602090815260409182902060020180547fffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffff166a010000000000000000000063ffffffff96909616420164ffffffffff811696870291909117909155915193845290935090917f1ac7b35bca40be2823e4880f1b2e9ef02fa4c7bd62aed73c2ea2959232a1f9f5910160405180910390a2919050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812054610100900460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f6113e87f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a83613e0d565b6002545f907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16612a8e6119a4565b01905090565b5f6113e8827f6d696e745f74746c000000000000000000000000000000000000000000000000613e39565b5f6113e88263ffffffff613ec8565b612ad7816113bd565b612b25576040517ff731555300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401611cf2565b50565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812060028101549091908190612b7b9064ffffffffff8082169165010000000000900416612b76611b9b565b613b34565b915091508164ffffffffff165f03612b935750505050565b6002830180547fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000064ffffffffff8416021790555f612bd885611725565b9050806dffffffffffffffffffffffffffff165f03612bf8575050505050565b5f612c2386612c1e64ffffffffff87166dffffffffffffffffffffffffffff8616615020565b613ed6565b9050806dffffffffffffffffffffffffffff165f03612c4457505050505050565b8573ffffffffffffffffffffffffffffffffffffffff167f4fb8bb7b0278c9f68d34ce501b521ecd2e3c1bc48fe37eda47db36da6defc7ef85612c8684613a15565b6040805164ffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a2505050505050565b5f6dffffffffffffffffffffffffffff821115612d16576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612d16576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80612d87612d8186611725565b85614036565b9150612d9282613a15565b9050827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115612e38576040517f37666f1d0000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b73ffffffffffffffffffffffffffffffffffffffff949094165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081166dffffffffffffffffffffffffffff8481167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384160390921617909155600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831684900390921691909117905593915050565b5f612f0e612f08846122a1565b83614064565b73ffffffffffffffffffffffffffffffffffffffff9093165f90815260066020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821686900381167fffff0000000000000000000000000000000000000000000000000000000000009283161790925560028054808416879003909316929091169190911790555090919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090205460ff16612b25576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421015612b25576040517f1526e62e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6130808361179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106130ac57505050565b5f827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166130d78561168b565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160190508181111561313e576040517f5033ec120000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401611cf2565b50505050565b5f61271063ffffffff831664e8d4a5100002611819565b5f6128b56301e1338067ffffffffffffffff851663ffffffff851602046140b2565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff84160264e8d4a50fff015b049392505050565b5f6113e8826fffffffffffffffffffffffffffffffff613ec8565b5f6113e8827f6d696e745f64656c617900000000000000000000000000000000000000000000613e39565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161322a919061505e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6128b5837f76616c696461746f7273000000000000000000000000000000000000000000008461412b565b5f6113e8827f6d696e745f726174696f00000000000000000000000000000000000000000000613e39565b5f8163ffffffff168363ffffffff161061330357816128b5565b5090919050565b425f805b83811015613480575f811180156133a0575088886001830381811061333557613335615133565b905060200201602081019061334a9190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1689898381811061337257613372615133565b90506020020160208101906133879190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1611155b156133d7576040517f139f3c9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134488e8e8e8e8e8e8e888181106133f1576133f1615133565b90506020020160208101906134069190614bd8565b8d8d8981811061341857613418615133565b905060200201358c8c8a81811061343157613431615133565b90506020028101906134439190615160565b6141cb565b156134785761346f8388888481811061346357613463615133565b9050602002013561438d565b92508160010191505b60010161330e565b505f61348a61231e565b9050808210156134d0576040517f74e8306f0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611cf2565b50509b9a5050505050505050505050565b5f805b8281101561360d575f61350e85858481811061350257613502615133565b90506020020135613dba565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260076020908152604080832065ffffffffffff851684529091528120549192507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091169081900361357a575050613605565b73ffffffffffffffffffffffffffffffffffffffff87165f81815260076020908152604080832065ffffffffffff8716808552925280832080547fffff000000000000000000000000000000000000000000000000000000000000169055519684019690917f7318b8ec2c2570ee6068ac690bbda62f9d13d38d52e36e23e0885101a5ffab0791a350505b6001016134e4565b5073ffffffffffffffffffffffffffffffffffffffff9093165f90815260046020526040902060010180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808216869003167fffff0000000000000000000000000000000000000000000000000000000000009091161790555090919050565b5f61369683611725565b9050806dffffffffffffffffffffffffffff165f036136b457505050565b5f6136be8461179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106136eb5750505050565b5f6136f582613dfb565b9050826dffffffffffffffffffffffffffff16816dffffffffffffffffffffffffffff1610613725575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260408120600281015490919061376e9064ffffffffff80821691650100000000009004166143a9565b90508064ffffffffff168664ffffffffff161161378e5750505050505050565b8086038386035f6137e48a6137a1611b9b565b63ffffffff168564ffffffffff1685026dffffffffffffffffffffffffffff16816137ce576137ce614ed3565b046dffffffffffffffffffffffffffff16613ed6565b9050806dffffffffffffffffffffffffffff165f036138095750505050505050505050565b8973ffffffffffffffffffffffffffffffffffffffff167fadbe0a005ef4d2903a69fd3053b1c8dfc083430632f549a92a1d3bc648104ae361384a84613a15565b8561385485613a15565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948516815264ffffffffff93909316602084015292168183015290519081900360600190a250505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600460205260408120600281015490919061391d906138ff9064ffffffffff808216916f010000000000000000000000000000009004166143a9565b61391661390a611b9b565b63ffffffff164261438d565b42036143a9565b90508064ffffffffff168364ffffffffff1611613977576040517f17ea884500000000000000000000000000000000000000000000000000000000815264ffffffffff808516600483015282166024820152604401611cf2565b5080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909316620100000261ffff9093169290921782556002909101805464ffffffffff9092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090921691909117905550565b5f6113e8827f6d696e7465725f667265657a655f74696d650000000000000000000000000000613e39565b5f6113e882613a226112af565b6143c5565b5f61171a7f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e5f1b88888888604051602001613a639291906151c1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083019590955273ffffffffffffffffffffffffffffffffffffffff909316928101929092526060820152608081019190915260a0810185905260c0810184905260e001604051602081830303815290604052805190602001206143d0565b5f6128b58383614431565b5f6113e8613b31837f6d696e7465725f726174655f6d6f64656c000000000000000000000000000000613e39565b90565b5f805f613b4186866143a9565b90508564ffffffffff165f03613b5b575f92509050613bb5565b5f613b668242615201565b90508463ffffffff168164ffffffffff161015613b8857505f92509050613bb5565b8463ffffffff168164ffffffffff1681613ba457613ba4614ed3565b0493505063ffffffff841683020190505b935093915050565b5f6113e8827f7570646174655f636f6c6c61746572616c5f696e74657276616c000000000000613e39565b5f8163ffffffff168363ffffffff161161330357816128b5565b5f6113e882613c0f6112af565b614462565b5f6113e8827f7570646174655f636f6c6c61746572616c5f7468726573686f6c640000000000613e39565b5f80613c4961446d565b5f5490915064ffffffffff740100000000000000000000000000000000000000009091041642148015613c9957505f5463ffffffff82811670010000000000000000000000000000000090920416145b15613cb75750505f546fffffffffffffffffffffffffffffffff1690565b613cbf6112af565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b5f6113e8827f70656e616c74795f726174650000000000000000000000000000000000000000613e39565b5f65ffffffffffff821115612d16576040517f069c6d4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6113e882613e086112af565b61456d565b5f6128b5837f6d696e74657273000000000000000000000000000000000000000000000000008461412b565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015613ea4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128b59190614f51565b5f81831061330357816128b5565b5f8172ffffffffffffffffffffffffffffffffffffff165f03613efa57505f6113e8565b5f613f036127ea565b90508063ffffffff165f03613f1b575f9150506113e8565b60035461271063ffffffff831672ffffffffffffffffffffffffffffffffffffff86160204906dffffffffffffffffffffffffffff908116820190811115613f7a5750506003546dffffffffffffffffffffffffffff90811681038116905b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff92831617905573ffffffffffffffffffffffffffffffffffffffff959095165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081169683167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216011695909517909455509192915050565b5f816dffffffffffffffffffffffffffff16836dffffffffffffffffffffffffffff161061330357816128b5565b5f817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161061330357816128b5565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161412157614121614ed3565b0495945050505050565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa15801561419f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141c3919061521f565b949350505050565b5f835f03614205576040517fda16d76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4264ffffffffff16841115614246576040517f0ff02cef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808b165f908152600860209081526040808320938916835292905220548085116142d7576040517f2e7a0bcb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024810186905260448101829052606401611cf2565b6142e0866113bd565b6142ed575f915050614380565b61433a866142ff8d8d8d8d8d8c613a27565b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061457892505050565b614347575f915050614380565b505073ffffffffffffffffffffffffffffffffffffffff808a165f90815260086020908152604080832093881683529290522083905560015b9998505050505050505050565b5f8164ffffffffff168364ffffffffff161061330357816128b5565b5f8164ffffffffff168364ffffffffff161161330357816128b5565b5f6128b58383614595565b5f6143d9611368565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026131a9565b5f6128b583836145cd565b5f805f614478611aff565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916144f4919061523e565b5f60405180830381855afa9150503d805f811461452c576040519150601f19603f3d011682016040523d82523d5f602084013e614531565b606091505b509150915081801561454557506020815110155b61454f575f614566565b61456681806020019051810190610bb69190614f51565b9250505090565b5f6128b5838361468e565b5f614584848484614725565b806141c357506141c3848484614774565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff84160264e8d4a50fff016131a9565b5f816fffffffffffffffffffffffffffffffff165f03614619576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161468857614688614ed3565b04612ccd565b5f816fffffffffffffffffffffffffffffffff165f036146da576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161468857614688614ed3565b5f815160400361474e5760208201516040830151614745868684846148bd565b925050506128b5565b5f61475a8585856148e6565b600581111561476b5761476b615259565b14949350505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff1685856040516024016147a1929190615286565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251614822919061523e565b5f60405180830381855afa9150503d805f811461485a576040519150601f19603f3d011682016040523d82523d5f602084013e61485f565b606091505b509150915081801561487357506020815110155b80156148b3575080517f1626ba7e00000000000000000000000000000000000000000000000000000000906148b19083016020908101908401614f51565b145b9695505050505050565b5f806148cb86868686614920565b60058111156148dc576148dc615259565b1495945050505050565b5f805f6148f3858561495b565b90925090505f82600581111561490b5761490b615259565b1461491657816148b3565b6148b3868261499f565b5f805f61492e8686866149da565b90925090505f82600581111561494657614946615259565b14614951578161171a565b61171a878261499f565b5f8082516041146149715750600290505f614998565b6020830151604084015160608501515f1a919061499087848484614a21565b945094505050505b9250929050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146128b35760056128b5565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416614a1387838884614a21565b935093505050935093915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614a565750600390505f614b25565b8460ff16601b14158015614a6e57508460ff16601c14155b15614a7e5750600490505f614b25565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614ace573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615614b1c575f81614b20565b60015f5b915091505b94509492505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614b51575f80fd5b919050565b5f8060408385031215614b67575f80fd5b614b7083614b2e565b946020939093013593505050565b5f805f60608486031215614b90575f80fd5b614b9984614b2e565b95602085013595506040909401359392505050565b5f8060408385031215614bbf575f80fd5b82359150614bcf60208401614b2e565b90509250929050565b5f60208284031215614be8575f80fd5b6128b582614b2e565b5f8083601f840112614c01575f80fd5b50813567ffffffffffffffff811115614c18575f80fd5b6020830191508360208260051b8501011115614998575f80fd5b5f805f805f805f805f8060c08b8d031215614c4b575f80fd5b8a35995060208b013567ffffffffffffffff80821115614c69575f80fd5b614c758e838f01614bf1565b909b50995060408d0135985060608d0135915080821115614c94575f80fd5b614ca08e838f01614bf1565b909850965060808d0135915080821115614cb8575f80fd5b614cc48e838f01614bf1565b909650945060a08d0135915080821115614cdc575f80fd5b50614ce98d828e01614bf1565b915080935050809150509295989b9194979a5092959850565b5f805f805f8060a08789031215614d17575f80fd5b614d2087614b2e565b955060208701359450604087013567ffffffffffffffff811115614d42575f80fd5b614d4e89828a01614bf1565b979a9699509760608101359660809091013595509350505050565b5f5b83811015614d83578181015183820152602001614d6b565b50505f910152565b5f8151808452614da2816020860160208601614d69565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e06020840152614e1060e084018a614d8b565b8381036040850152614e22818a614d8b565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b81811015614e8257835183529284019291840191600101614e66565b50909c9b505050505050505050505050565b5f60208284031215614ea4575f80fd5b5035919050565b5f8060408385031215614ebc575f80fd5b614ec583614b2e565b9150614bcf60208401614b2e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b600181811c90821680614f1457607f821691505b602082108103614f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215614f61575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115614fcc57614fcc614f68565b5092915050565b64ffffffffff818116838216019080821115614fcc57614fcc614f68565b5f60208284031215615001575f80fd5b81516fffffffffffffffffffffffffffffffff811681146128b5575f80fd5b72ffffffffffffffffffffffffffffffffffffff82811682821681810283169291811582850482141761505557615055614f68565b50505092915050565b5f8083545f60018260011c9150600183168061507b57607f831692505b602080841082036150b3577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b8180156150c757600181146150fa57615125565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615125565b5f8a8152602090205f5b8681101561511d5781548b820152908501908301615104565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615193575f80fd5b83018035915067ffffffffffffffff8211156151ad575f80fd5b602001915036819003821315614998575f80fd5b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151ee575f80fd5b8260051b80858437919091019392505050565b64ffffffffff828116828216039080821115614fcc57614fcc614f68565b5f6020828403121561522f575f80fd5b815180151581146128b5575f80fd5b5f825161524f818460208701614d69565b9190910192915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b828152604060208201525f6141c36040830184614d8b56fea26469706673582212202fb093d96f0c6cdac2540fc9fd6bdc6dd1d2f7ff81d14968d63024c63999cf4364736f6c63430008170033

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

000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0

-----Decoded View---------------
Arg [0] : ttgRegistrar_ (address): 0x975Bf5f212367D09CB7f69D3dc4BA8C9B440aD3A
Arg [1] : mToken_ (address): 0x0c941AD94Ca4A52EDAeAbF203b61bdd1807CeEC0

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a
Arg [1] : 0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.