Technical Documentation

Minting & Burning (MinterGateway)

Complete documentation of the MinterGateway contract, the central hub for minting and burning, managing minter collateral, and tracking debt obligations.

Overview

Where money is born.

The MinterGateway contract is the central component within the M0 Protocol responsible for orchestrating the entire lifecycle of $M. It governs the processes for minting and burning, manages the offchain collateral system that backs the supply, and tracks the debt obligations of Minters. This contract acts as the primary interface for Minters and Validators, ensuring that issuance is always securely collateralized and transparently managed according to protocol rules defined by M0 Governance.

Its key responsibilities include:

  • Managing Minter activation, deactivation, and operational states.
  • Overseeing the collateral update process, including verification of offchain assets by Validators.
  • Enforcing the structured minting and burning procedures.
  • Calculating and applying interest, debt, and penalties for Minters.
  • Facilitating the collateral retrieval process for Minters.
  • Synchronizing its interest index with the MToken contract to maintain system-wide consistency.

Minting and Burning Process

The creation (minting) and destruction (burning) of $M tokens are strictly controlled by the MinterGateway.

Minting $M

The minting process is designed with security checkpoints involving a proposal, a delay, and execution:

  1. Update Collateral (Prerequisite): Before proposing a mint, a Minter must have an up-to-date and sufficient verified collateral value on record.
  2. Propose Mint: An active Minter initiates a mint request by calling proposeMint(): This creates a mint proposal with a unique mintId_. The proposal specifies the amount of $M to be minted and the recipient address. This step allows Validators to review pending mints.
    function proposeMint(uint256 amount, address destination) external returns (uint48 mintId_);
    
  3. Delay Period: A mandatory delay period (MINT_DELAY), set by governance, must pass after a mint is proposed before it can be executed. This window allows Validators to intervene (e.g., cancel the mint or freeze the Minter) if they detect any issues.
  4. Execute Mint: After the MINT_DELAY has elapsed, and before the proposal expires (within MINT_TTL - Time To Live, also set by governance), the Minter can execute the mint by calling mintM(): This function mints the specified amount of $M to the destination address, provided the proposal is still valid, hasn't been canceled, the Minter isn't frozen, and the mint doesn't violate collateralization requirements.
    function mintM(uint256 mintId) external;
    

The MINT_DELAY and MINT_TTL mechanisms create a secure time window for oversight, preventing immediate, unreviewed mints and ensuring proposals don't remain executable indefinitely.

Burning $M

$M tokens can be burned to reduce a Minter's outstanding debt. Any $M holder can initiate a burn against a specific Minter's debt:

function burnM(address minter, uint256 amount) external;
  • For Active Minters: Burning $M reduces the principal of their interest-accruing debt (activeOwedM).
  • For Deactivated Minters: Burning $M reduces their fixed inactiveOwedM. This serves as an informal liquidation mechanism, allowing the market to help settle the obligations of a Minter who has exited the system. The _repayForDeactivatedMinter internal function handles this logic.

Collateral System

$M is backed by offchain collateral, typically real-world assets like U.S. Treasury Bills held in Special Purpose Vehicles (SPVs). The MinterGateway manages the onchain representation and verification of this collateral.

Offchain Backing & Onchain Representation

Minters are responsible for maintaining sufficient eligible collateral offchain. The value of this collateral is reported onchain through the MinterGateway.

Validator Verification

Trusted, independent Validators play a crucial role in verifying the Minters' offchain collateral. They periodically assess the collateral and provide cryptographic signatures attesting to its value.

Update Process

Minters must regularly update their onchain collateral value to reflect the current state of their offchain reserves. This is done by calling the updateCollateral function:

function updateCollateral(
    uint256 collateral,
    uint256[] calldata retrievalIds,
    bytes32 metadataHash,
    address[] calldata validators,
    uint256[] calldata timestamps,
    bytes[] calldata signatures
) external returns (uint40 minTimestamp_);

Key aspects of the collateral update process:

  • Frequency: Minters must update their collateral within a governance-defined interval (e.g., UPDATE_COLLATERAL_INTERVAL, typically daily). Failure to do so results in their onchain collateral value being considered zero until a new valid update is provided.
  • Validator Signatures: A collateral update requires signatures from multiple approved Validators, meeting a minimum threshold set by governance (e.g., UPDATE_COLLATERAL_THRESHOLD).
    • Signatures must be provided with validator addresses in ascending order to prevent duplicates.
    • Each signature includes a timestamp which must be newer than the last signature from that specific validator for that Minter and must not be in the future. This prevents replay attacks.
    • Signatures use EIP-712 typed data specific to each validator.
  • Collateral Expiration: If a Minter fails to update their collateral within the UPDATE_COLLATERAL_INTERVAL, their effective onchain collateral value is treated as zero for all protocol calculations, potentially leading to penalties or an inability to mint.
  • Collateralization Caps: Minters must maintain a collateralization ratio above the governance-set MINT_RATIO. This ratio dictates the maximum amount of $M a Minter can have outstanding relative to their verified collateral.
  • Earliest Update Timing: A new collateral update's effective timestamp must be greater than:
    • The Minter's last update timestamp.
    • The timestamp of the Minter's latest proposed collateral retrieval.
    • block.timestamp - UPDATE_COLLATERAL_INTERVAL to ensure updates are reasonably current.

Collateral Retrieval

Minters can retrieve excess collateral from their backing SPV, provided the retrieval does not compromise their ability to back their outstanding $M debt. This process is managed onchain through the MinterGateway and requires Validator oversight.

  1. Propose Retrieval: The Minter initiates a request to retrieve a specific amount of collateral by calling proposeRetrieval():
    function proposeRetrieval(uint256 collateral) external returns (uint48 retrievalId_);
    

    This creates a pending retrieval proposal with a unique, sequential retrievalId_. The proposed retrieval amount is tracked in the _pendingCollateralRetrievals mapping and is immediately deducted from the Minter's effective collateral value for all ongoing calculations, ensuring the Minter remains adequately collateralized. The system prevents proposals that would lead to undercollateralization.
  2. Validator Inclusion & Resolution: For a pending retrieval to be finalized, it must be explicitly included and approved by Validators in a subsequent updateCollateral transaction.
    • When Validators sign off on a Minter's collateral update, their signature digest includes the list of retrievalIds they are approving for resolution.
    • During the updateCollateral call, the MinterGateway processes these approved retrievalIds. The corresponding amounts are then considered formally retrieved, and their values are no longer deducted from the Minter's available collateral.

This multi-step process ensures that collateral can only be effectively retrieved with explicit, signed approval from Validators as part of a standard, verified collateral update, maintaining the integrity of $M's backing. Multiple retrieval proposals can be resolved within a single updateCollateral transaction.

Debt, Interest, and Penalties for Minters

Minters incur debt for the $M tokens they mint and are charged interest on this debt. The MinterGateway manages this system.

Debt and Interest

  • Debt Accrual: When a Minter mints $M, they incur an equivalent amount of debt.
  • Continuous Indexing: Minter debt, similar to MToken earning balances, is tracked using a continuous indexing system. The debt is stored as a principal amount that grows over time as interest accrues.
  • Interest Rate Model: The interest rate charged to Minters (MINTER_RATE_MODEL) is determined by an external smart contract, the address of which is set by M0 Governance and stored in the TTGRegistrar.
  • activeOwedM: This term represents the current total value of an active Minter's obligations, including all accrued interest. It is calculated by applying the current minter index to their principal debt.

Total Owed $M Accounting

The MinterGateway tracks various categories of Minter debt:

  • Active Owed $M: The sum of interest-accruing debt from all active Minters.
  • Inactive Owed $M: The sum of fixed, non-interest-accruing debt from all deactivated Minters.
  • Total Owed $M: The aggregate of activeOwedM and inactiveOwedM.
  • Excess Owed $M: The difference between totalOwedM (interest generated from Minters) and the total supply of $M (which grows at the earner rate). This excess, if positive, represents protocol revenue and is minted directly to the DistributionVault (TTGVault) during index updates.

Penalties

The protocol imposes penalties on Minters for failing to adhere to specific operational requirements, ensuring system discipline:

  1. Missed Collateral Updates Penalty: If a Minter fails to update their collateral within the UPDATE_COLLATERAL_INTERVAL, a penalty is applied.
    • Calculation: PENALTY_RATE * principalOfActiveOwedM * missedIntervals.
    • This penalty is charged once per missed interval.
  2. Undercollateralization Penalty: If a Minter's activeOwedM exceeds the maximum allowed by their verified collateral and the MINT_RATIO, they are penalized.
    • Calculation: PENALTY_RATE * principalOfExcessOwedM * (timeSpan / UPDATE_COLLATERAL_INTERVAL).
    • This penalty is proportional to the amount and duration of the undercollateralization.

Penalties are added to the Minter's debt.

Minter Lifecycle and Management

Minters are permissioned entities that interact with the MinterGateway to issue and manage $M. Their lifecycle within the protocol is characterized by several distinct statuses:

  1. Approved: Minters are initially whitelisted by the M0 Two-Token Governance (TTG) system. At this stage, they are recognized by the protocol but cannot yet perform minting operations.
  2. Active: An approved Minter must explicitly activate their status by calling activateMinter() on the MinterGateway.
    function activateMinter(address minter_) external;
    
    This function can only be called by the Minter themselves and transitions them to an operational state, allowing them to propose collateral updates and mint $M. The active status is stored locally within MinterGateway for gas efficiency.
  3. Frozen: An active Minter can be temporarily restricted from minting $M by Validators (via freezeMinter()). During this state, the Minter can still burn $M to reduce their debt and update their collateral but cannot propose new mints. The freeze duration is determined by governance.
  4. Deactivated: A Minter can be deactivated if they are removed from the governance-approved list. Deactivation is a permanent state, initiated by a call to deactivateMinter().
    function deactivateMinter(address minter_) external returns (uint240 inactiveOwedM_);
    
    This function can be called if the Minter is no longer approved by TTG. Upon deactivation:
    • The Minter's outstanding debt, including accrued interest, is converted into a fixed inactiveOwedM amount which no longer accrues interest.
    • The Minter can no longer mint $M or propose collateral retrievals.
    • Their collateral remains to back their inactiveOwedM.
    • A deactivated Minter cannot be reactivated. This permanence optimizes gas by eliminating further status checks against the TTGRegistrar.

Validator Roles and Security Controls

Validators are independent, trusted entities that form a critical security layer for the minting and burning processes within the MinterGateway. Their responsibilities and powers include:

Core Responsibilities

  • Collateral Verification: Validators verify Minters' offchain collateral and provide cryptographic signatures for updateCollateral transactions. These signatures attest to the value and existence of the assets backing $M.
  • Timestamping: Signatures from Validators must include a secure timestamp to prevent replay attacks and ensure the freshness of collateral data.
  • Enabling Offchain-Onchain Bridge: Validators act as the crucial link ensuring the onchain representation of collateral accurately reflects the state of offchain assets.

Validator Signature Verification

The signature verification system includes several security features:

  • Multi-signature Requirement: Requires multiple validator signatures for collateral updates.
  • Ordered Validator Addresses: Validator addresses must be provided in ascending order to prevent duplicates.
  • Timestamp Validation: Each signature includes a timestamp that must be:
    • Newer than the last signature from that validator for that minter.
    • Not in the future.
    • Not zero.
  • Replay Protection: The system tracks the last used timestamp for each validator-minter pair.
  • Signature Format: Uses EIP-712 typed data signatures that are validator-specific.

Security Controls & Emergency Powers

Validators are empowered with specific functions to intervene and protect the protocol:

  1. Cancel Mint Proposal: If Validators deem a pending mint proposal to be suspicious, invalid, or potentially harmful to the protocol, they can cancel it before execution:
    function cancelMint(address minter, uint256 mintId) external onlyApprovedValidator;
    

    This action deletes the mint proposal, preventing the Minter from executing it.
  2. Freeze Minter: Validators can temporarily suspend a Minter's ability to mint new $M tokens:
    function freezeMinter(address minter) external onlyApprovedValidator returns (uint40 frozenUntil_);
    

    This "freezes" the Minter for a duration specified by the governance parameter MINTER_FREEZE_TIME. A frozen Minter can still burn $M and update collateral but cannot propose or execute new mints until the freeze period expires.

These controls allow Validators to act as a rapid response mechanism against potential threats or misbehavior related to $M issuance.

Timing Constraints

The system enforces several timing-related constraints:

  • Collateral Update Frequency: Minters must update at least once per UPDATE_COLLATERAL_INTERVAL.
  • Mint Delay: Proposals must wait MINT_DELAY seconds before execution.
  • Mint TTL: Proposals expire after MINT_TTL seconds.
  • Earliest Collateral Update: The timestamp for a new update must be greater than:
    • The last update timestamp.
    • The latest proposed retrieval timestamp.
    • block.timestamp - UPDATE_COLLATERAL_INTERVAL.

Security Mechanisms

The MinterGateway implements several safety features:

  • Mint Delay & TTL (Time-To-Live): Creates a time window for validators to review and potentially cancel mints.
  • Collateralization Caps: Enforces maximum mint-to-collateral ratios (set by governance).
  • Multi-signature Requirement: Requires multiple validator signatures for collateral updates.
  • Strictly Ordered Validations: Validator addresses must be provided in ascending order.
  • Timestamp Verification: Prevents reuse of signatures and ensures freshness of collateral data.
  • Excess Yield Distribution: Any yield generated by minters beyond what's distributed to earners goes to the TTG Vault (also called the DistributionVault).

Emergency Measures

For extreme circumstances, the protocol includes additional safeguards:

Mint Proposal Cancellation

Validators can cancel suspicious mint proposals before they're executed, allowing them to quickly intervene if they detect potentially harmful minting activity.

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

Validator Freeze

Validators can freeze minters to stop them from minting tokens for a governance-defined period:

function freezeMinter(address minter_) external onlyApprovedValidator returns (uint40 frozenUntil_) {
    unchecked {
        _minterStates[minter_].frozenUntilTimestamp = frozenUntil_ = uint40(block.timestamp) + minterFreezeTime();
    }
    emit MinterFrozen(minter_, frozenUntil_);
}

Minter Deactivation

Governance can completely deactivate minters who have been removed from the approved list:

function deactivateMinter(address minter_) external onlyActiveMinter(minter_) returns (uint240 inactiveOwedM_) {
    if (isMinterApproved(minter_)) revert StillApprovedMinter();
    // Implementation details for deactivation process
}

Once deactivated, a minter cannot be reactivated, providing a permanent resolution for problematic minters.

Open Redemption

Anyone can burn $M tokens to repay a deactivated minter's debt:

function _repayForDeactivatedMinter(address minter_, uint240 maxAmount_) internal returns (uint240 amount_) {
    amount_ = UIntMath.min240(inactiveOwedMOf(minter_), maxAmount_);
    unchecked {
        _rawOwedM[minter_] -= amount_;
        totalInactiveOwedM -= amount_;
    }
}

This creates an informal liquidation mechanism for deactivated minters.

Index Synchronization with $M Token

The MinterGateway and the MToken contract both utilize a continuous indexing mechanism to track the accrual of interest -- for Minter debt in MinterGateway and for earner balances in MToken. It is crucial that these indices are synchronized.

Synchronization Mechanism

The MinterGateway ensures that whenever its own interest index for Minter obligations is updated, the MToken's earning index is also updated simultaneously. This is achieved within the MinterGateway.updateIndex() function:

function updateIndex() public override(IContinuousIndexing, ContinuousIndexing) returns (uint128 index_) {
    uint240 excessOwedM_ = excessOwedM();
    if (excessOwedM_ > 0) IMToken(mToken).mint(ttgVault, excessOwedM_); // Mint excess to TTG Vault

    // First, update the MinterGateway's own index and rate (for minter debt)
    index_ = super.updateIndex();

    // Then, explicitly trigger an update of the MToken's index and rate (for earner yield)
    IMToken(mToken).updateIndex();

    // Emit an event if this MinterGateway instance is also the MToken's MinterGateway
    // (This is relevant if MToken directly calls this contract for its own updates)
    if (address(this) == IMToken(mToken).minterGateway()) {
        emit MTokenIndexUpdated(IMToken(mToken).currentIndex(), IMToken(mToken).currentRate());
    }
}

This synchronized update ensures that the calculation of interest paid by Minters and yield distributed to Earners remains consistent and balanced across the protocol, reflecting the latest rates and system state.

Triggers for Index Updates

The updateIndex() function in MinterGateway (which in turn calls MToken.updateIndex()) is invoked during several key operations, ensuring indices are frequently updated:

  • When a Minter updates their collateral via updateCollateral().
  • When $M tokens are minted via mintM().
  • When $M tokens are burned via burnM().
  • When a Minter is deactivated via deactivateMinter().
  • When MinterGateway.updateIndex() is explicitly called by an external party.

This frequent synchronization is vital for the accurate accounting of debts, yields, and the overall economic stability of the M0 Protocol.

Copyright © M0 Foundation 2026