Technical Documentation

Wrapped M (wM)

Complete documentation of Wrapped M (wM), the non-rebasing ERC-20 wrapper that maintains yield-earning capabilities while providing DeFi compatibility.

Overview

What is Wrapped $M (wM)?

Wrapped $M (wM) is a non-rebasing ERC-20 wrapper for the rebasing and immutable $M token. It maintains yield-earning capabilities while providing compatibility with DeFi protocols that require standard ERC-20 tokens.

While the native $M token automatically increases balances as yield accrues, Wrapped $M maintains static balances where yield can be explicitly claimed. This makes Wrapped $M compatible with protocols that aren't designed to handle rebasing tokens, dramatically expanding the utility of $M in the broader DeFi ecosystem.

Why Wrapped M?

Every M0 extension eventually wants to connect to certain utilities such as DeFi protocols, on/off ramps, etc. In order to concentrate liquidity, that utility should always exist in the form of M0's stablecoin building block.

However, rebasing tokens automatically change their balances as yield accrues, which creates challenges for many DeFi protocols that expect static token balances. Wrapped $M solves this by:

  1. Maintaining fixed token balances that don't automatically increase
  2. Separately tracking and allowing manual claiming of accrued yield
  3. Preserving the full economic benefits of the M0 platform while improving protocol compatibility
  4. Enabling protocol-specific administration of earning capabilities

Key Features

  • DeFi Compatibility: Works with lending platforms, AMMs, and other protocols that require standard ERC-20 behavior
  • Yield Preservation: Maintains yield-earning capabilities of the underlying $M token
  • Value & Solvency: wM aims for a direct value link to $M for earners. The target value for an earning token is its underlying $M principal plus accrued yield:1 earning wM+wM yield 1 M+Accrued Yield1 \text{ }\text{earning }\text{wM} + \text{wM yield } \approx 1 \text{ }\text{M} + \text{Accrued Yield}
    Critically, the entire system maintains a solvency invariant, ensuring it's always fully backed. The total $M held by the wM contract is always greater than or equal to the total wM supply plus all accrued yield owed to earners:M Balance(wM contract)totalNonEarningSupply+projectedEarningSupplyTotal wM Liabilities, aka wM supply + accrued yield\text{M Balance}_{(\text{wM contract})} \ge \underbrace{\text{totalNonEarningSupply} + \text{projectedEarningSupply}}_{\text{Total wM Liabilities, aka wM supply + accrued yield}}
    This guarantee is maintained through the system's design, where yield earned on $M held for non-earning wM holders, along with minor positive rounding effects during transfers, accumulates as "excess" reserves within the contract.
  • Flexible Yield Options: Configurable claiming and recipient mechanisms
  • Delegated Administration: Trusted admins can manage earner status without governance
  • Protocol Value Capture: System design naturally accumulates excess reserves that benefit the broader protocol ecosystem

Comparison to $M

Feature$MWrapped M
Balance BehaviorAutomatically increases (rebasing)Remains static until explicit claim
Earning MechanismImplicit (balances grow)Explicit (yield accumulates separately)
Yield RealizationAutomatic with balance updatesManual claiming required
DeFi CompatibilityLimited due to rebasingWide compatibility with standard ERC-20
Recipient OptionsLimitedConfigurable yield recipients
AdministrationGovernance onlyGovernance + Delegated admins

Core Concepts

Dual Mode: Earning vs. Non-Earning

The wM token operates in two distinct modes for each account:

Non-Earning Mode

  • Default state for new accounts
  • Balances remain static (standard ERC-20 behavior)
  • No yield accrues on these tokens
  • Simpler accounting with direct token amounts

Earning Mode

  • Must be explicitly activated (requires approval via Governance or EarnerManager admin designation)
  • Balances remain static in storage
  • Represents a growing value based on a principal amount and an index, using an accounting model analogous to the native $M token.
  • Crucially, the index used is derived from the underlying $M token (wM does not maintain its own independent index).
  • Yield accumulates based on this principal and derived index, and can be claimed separately.

Users can switch between Non-Earning and Earning modes if they meet the requirements for earning.

Account Status

Each wM account maintains several key properties tracked internally within the WrappedMToken contract:

  • Balance: The current wM token balance held by the account. This value remains static between operations and excludes any unclaimed yield.
  • Earning Status: A boolean flag indicating whether the account is currently in Earning Mode (true) or Non-Earning Mode (false).
  • Earning Principal: For accounts in Earning Mode, this stores the base principal amount used for yield calculations. This value is derived from the account's balance when it starts earning and remains constant while earning.
  • Claim Recipient: An optional address specified by the user via setClaimRecipient() to receive their claimed yield. If not set by the user, and no Governance override exists, yield is sent to the account owner itself.
  • Earner Details: A boolean flag indicating if settings apply when an account's earning status is managed by an EarnerManager admin.
    • If true, it implies the EarnerManager contract holds details (managing admin address, fee rate) for this account.
    • When a fee rate is set by the admin, a portion of the claimed yield (calculated as yield * feeRate / 10000) is sent to that admin upon claiming, and the remaining net yield goes to the designated claim recipient (user-set, governance-set, or default owner).

Principal, Balance, and Yield Relationship:

When an account enters Earning Mode (startEarningFor), its current balance is converted into its initial earningPrincipal.

This conversion is performed by the IndexingMath.getPrincipalAmountRoundedDown(balance, currentIndex) function, which calculates the principal amount corresponding to the balance at the currentIndex, taking into account the system's fixed-point scaling for the index and applying rounding rules that slightly favor the protocol.

This earningPrincipal remains fixed while the account continues in Earning Mode. As the global currentIndex (derived from the $M token's index) increases over time, the value represented by this fixed principal grows.

The fundamental relationship is:

Value Represented=earningPrincipal×currentIndex\text{Value Represented} = \text{earningPrincipal} \times \text{currentIndex}

The account's stored balance only increases when yield is explicitly claimed. Therefore, the accruedYield represents the difference between the current value represented by the principal and the static stored balance:

accruedYield=earningPrincipal×currentIndexbalance\text{accruedYield} = \text{earningPrincipal}\times\text{currentIndex}-\text{balance}

So, the target state before claiming is effectively:

balance+accruedYieldearningPrincipal×currentIndex\text{balance} + \text{accruedYield}\approx\text{earningPrincipal}\times\text{currentIndex}

Calling the claimFor() function calculates this accruedYield. It then handles potential fee deductions if hasEarnerDetails is true (sending the fee portion to the managing admin). The remaining net yield is transferred to the designated claimRecipient. If the claimRecipient is the account owner itself, their stored balance is increased by this net yield amount. This operation "catches up" the account's stored balance to reflect the value earned up to that point (minus any fees paid).

Yield Generation and Claiming

How Yield Accrues

For accounts in earning mode, yield accrues based on:

  • The account's principal amount
  • The global wM index (derived from the $M token's index)
  • The time elapsed since the last index update

The formula is:

accruedYield=(principal×currentIndex)balanceaccruedYield=(principal\times currentIndex) - balance

Where:

  • principal is the stored earning principal for the account
  • currentIndex is the current wM index
  • balance is the current token balance

Claiming Process

When yield is claimed:

  1. The accrued yield is calculated based on the current index
  2. The account's balance is provisionally increased by the full yield amount
  3. If the account's earning status is managed by an active EarnerManager admin with a configured fee rate, a percentage of the yield is deducted and sent to that admin as a fee
  4. The remaining yield (minus any fees) is transferred to the designated recipient (or remains with the account owner if they are the recipient)

Claiming doesn't change the principal amount, only the final balance reflects the net yield received.

Index Mechanism

Rather than maintaining its own independent index, wM derives its index from the $M token's index:

When Earning is Enabled

The wM index is calculated as:

wMindex=(Mcurrentindex / enableMIndex)×disableIndexwM_{index} = (M_{current index} \text{ }/ \text{ }enableMIndex) \times disableIndex

Where:

  • M current index is the current index from the $M token
  • enableMIndex is the $M index when earning was enabled
  • disableIndex is the previous wM index (if earning was previously disabled)

When Earning is Disabled

The wM index becomes static:

wMindex=disableIndexwM_{index} = disableIndex

This approach ensures that wM's yield calculation accurately reflects the underlying $M token's yield performance.

Yield Recipients

The wM system provides flexible options for directing claimed yield:

Default Behavior

By default, yield is claimed to the same account that generated it.

Custom Recipients

Account holders can specify a different address to receive their yield by calling setClaimRecipient().

Governance Overrides

The TTG governance can set override recipients for specific accounts through the Registrar.

Priority Order

When determining where to send claimed yield:

  1. User-specified recipient (if set)
  2. Governance-specified override (if set)
  3. The account itself (default)

This flexibility allows for a variety of yield utilization strategies.

System Invariants and Excess

The wM system is designed around a critical solvency invariant to ensure it's always fully backed by the underlying $M token.

Balance Invariant: The total $M token balance held by the WrappedMToken contract must always be greater than or equal to the sum of all wM tokens in circulation plus all accrued yield owed to earners.

Expressed conceptually:

M Balance(wM contract)Total wM Supply+Total Accrued Yield\text{M Balance}_{(\text{wM contract})} \ge \text{Total wM Supply} + \text{Total Accrued Yield}

Expressed using contract variables:

M Balance(wM contract)totalNonEarningSupply+projectedEarningSupplyTotal wM Liabilities\text{M Balance}_{(\text{wM contract})} \ge \underbrace{\text{totalNonEarningSupply} + \text{projectedEarningSupply}}_{\text{Total wM Liabilities}}

How the Invariant is Maintained:

Crucially, the M Balance held by the WrappedMToken contract is not static. Since the WrappedMToken contract itself is registered as an $M earner (as shown in the $M dashboard where the wM contract is the largest holder), the underlying $M tokens it holds continuously accrue yield according to the $M token's rebasing mechanism.

This intrinsic yield generation on the entire pool of $M tokens held within the wM contract is fundamental to maintaining the solvency invariant. It ensures the $M balance grows naturally over time, actively keeping assets (M held) ahead of liabilities (wM supply + accrued yield).

Excess Reserves:

The difference between the actual M Balance held by the contract and the total calculated liabilities (totalNonEarningSupply + projectedEarningSupply) constitutes the "excess" reserves within the system. This excess primarily accumulates from two sources explained further below:

  1. The yield generated by the underlying $M tokens that back the balances of non-earning wM holders (as this yield isn't owed back to them).
  2. Minor positive rounding effects during various token operations (transfers, wrapping/unwrapping, starting/stopping earning).

This invariant and the resulting excess ensure that the system remains fully solvent, can always honor unwrap requests, and captures additional value for the protocol ecosystem.

Features and Operations

Basic Token Operations

Wrapping $M to wM

Users can deposit $M tokens to receive an equivalent amount of wM:

wrap(recipient, amount)

The $M tokens are held by the wM contract and can potentially earn yield.

Unwrapping wM to M

Users can burn wM tokens to receive the equivalent amount of M:

unwrap(recipient, amount)

Any accrued yield remains in the user's wM balance if they're in earning mode.

Gasless Operations

The contract supports wrapping with permits for improved UX:

wrapWithPermit(recipient, amount, deadline, signature)

Standard ERC-20 Operations

All standard ERC-20 functions are supported, including:

  • transfer(to, amount)
  • transferFrom(from, to, amount)
  • approve(spender, amount)
  • balanceOf(account)

Earning Management

Starting to Earn

Approved accounts can activate earning status:

startEarningFor(account)

This converts their balance to a principal amount that will begin accruing yield.

Stopping Earning

Accounts can deactivate earning status (or anyone can do this for an account that's no longer approved):

stopEarningFor(account)

This claims any outstanding yield and converts the balance back to non-earning.

Global Earning Status

The wM contract itself must be an approved earner in the $M token system. Earning can be enabled or disabled for the entire system:

enableEarning()
disableEarning()

A key improvement in wM v2 is the ability to re-enable earning after it has been disabled.

Yield Management

Claiming Yield

Accrued yield can be claimed at any time:

claimFor(account)

This increases the account's wM balance by the yielded amount and triggers appropriate transfers based on recipient settings.

Setting Claim Recipients

Users can direct their yield to another address:

setClaimRecipient(recipient)

This allows for flexible yield strategies without changing the underlying balance.

Checking Accrued Yield

Several view functions allow users and integrations to check the yield that has accrued for an earning account but has not yet been claimed:

  • accruedYieldOf(account):
    • Returns the amount of wM tokens earned as yield based on the account's earningPrincipal and the time elapsed (represented by the increase in the currentIndex) since the last claim or since earning started.
    • This represents only the unclaimed portion of the yield. It does not include yield that has already been claimed and added to the balance.
    • If the account is not in Earning Mode, this returns 0.
  • balanceWithYieldOf(account):
    • Returns the sum of the account's current stored balanceOf plus the currently claimable accruedYieldOf.
    • This function effectively shows the total wM value attributable to the account at that moment, representing what the balance would be if the accrued yield were claimed instantly (before considering any fees or alternate claim recipients).

Effect of Claiming: When claimFor(account) is successfully executed, the calculated accrued yield is processed. If the yield (net of fees) is directed to the account owner, their stored balanceOf increases. Consequently, immediately after a claim, accruedYieldOf will return 0 (or a negligible amount due to block timing), as the yield is no longer "accrued but unclaimed". balanceWithYieldOf will then equal balanceOf. New yield will start accumulating again as time passes and the currentIndex increases further.

Excess Management & Value Capture

Concept: The wM contract holds underlying $M tokens. Because the wM contract itself earns yield on all these $M tokens, but wM yield is only accrued for earning-mode wM holders, a surplus (or "excess") of $M tokens naturally accumulates within the contract over time. This primarily comes from the $M yield earned on tokens backing non-earning wM balances.

M and WrappedM are sharing the same Earners list. An earner of $M is an earner of WrappedM.

Claiming Excess: This accumulated excess M, representing value captured by the protocol, can be claimed and transferred out using the claimExcess() function.

claimExcess() returns (uint240 claimed_)

Destination: The excessDestination address, set during deployment, receives these claimed tokens. Typically, this is the Distribution Vault, directing this value back to the protocol ecosystem and ultimately benefiting Zero token holders.

Admin Operations via EarnerManager

The EarnerManager contract empowers designated admins ('special users') with the delegated authority to approve wM earners, managing status for potentially many accounts outside the standard governance process.

Setting Earner Status

Admins can enable accounts for earning and set fee rates:

setEarnerDetails(account, status, feeRate)

Bulk Operations

Multiple accounts can be managed in a single transaction:

setEarnerDetails(accounts[], statuses[], feeRates[])

Checking Earner Status

Various functions provide information about earner status:

earnerStatusFor(account)
getEarnerDetails(account)

Technical Design

Account Model

The wM contract uses a sophisticated account model to efficiently track balances and yield information:

struct Account {
    bool isEarning;         // Whether the account is in earning mode
    uint240 balance;        // Current token balance (excluding yield)
    uint112 earningPrincipal; // Principal amount for earning accounts
    bool hasClaimRecipient; // Whether a custom recipient is set
    bool hasEarnerDetails;  // Whether admin fee settings exist
}

This structure packs related data together to minimize storage costs while maintaining all necessary information for the dual-mode system.

Principal-Based Accounting

A key aspect of wM's design is its principal-based accounting system for earning accounts:

Principal Conversions

When an account starts earning, its balance is converted to a principal amount:

principal = balance / currentIndex (rounded down)

This principal remains constant while the account is earning, while its value in tokens grows with the index.

Present Value Calculation

The current token value of a principal is:

presentValue = principal * currentIndex

This calculation is used when checking balances and claiming yield.

Rounding Strategy

The contract employs consistent rounding rules:

  • When converting from tokens to principal (adding to earning), principal is rounded DOWN
  • When calculating present value (for balance displays), the value is exact
  • When subtracting from earning accounts, principal is rounded UP

These rules slightly favor the protocol, creating a small buffer of excess tokens that enhances stability.

Supply Tracking

The contract maintains precise tracking of token supply:

  • totalNonEarningSupply: Sum of all non-earning account balances
  • totalEarningSupply: Sum of all earning account balances (excluding accrued yield)
  • totalEarningPrincipal: Sum of all earning account principals
  • Total supply = totalNonEarningSupply + totalEarningSupply
  • projectedEarningSupply: The total earning supply if all accrued yield was claimed

These values are used for various calculations, including excess determination.

Transfer Mechanics

The transfer system handles different scenarios based on account earning status:

Same Status Transfers

  • Between earning accounts: Converts the amount to principal using the current index
  • Between non-earning accounts: Simple balance transfers without conversion

Cross-Status Transfers

  • From earning to non-earning: Converts from principal to tokens
  • From non-earning to earning: Converts from tokens to principal
  • Affects total earning and non-earning supply tracking

Each transfer type ensures that exact token amounts move between accounts while maintaining proper accounting in the appropriate mode.

Fee Mechanism

When enabled by an admin through the EarnerManager, fees can be taken from yield:

  • Fees are a percentage (in basis points, max 10,000) of claimed yield
  • Fee amounts are transferred to the admin who approved the account
  • If an admin is removed from the admin list, their fee settings become invalid
  • Fees are processed during the claim operation

This creates an incentive for admins to onboard and manage accounts in the system.

Excess Mechanism

The WrappedMToken (wM) contract is designed to naturally accumulate "excess" $M tokens over time. This excess represents the $M tokens held by the contract above and beyond what is required to fully back all circulating wM tokens and their accrued yield entitlements. This mechanism enhances system stability and creates a value capture opportunity for the protocol.

Core Principle: Yield Mismatch

The fundamental driver of excess accumulation stems from a mismatch in yield earning:

  1. wM Contract Earns $M Yield: As confirmed by its status as a major $M holder and its requirement to be an approved M earner (enableEarning requires _isThisApprovedEarner), the entire balance of $M tokens held within the WrappedMToken contract continuously earns yield according to the $M token's native rebasing mechanism.
  2. Not All wM Holders Earn wM Yield: However, only wM holders whose accounts are explicitly set to Earning Mode (via Governance or EarnerManager approval) accrue a corresponding claimable wM yield entitlement based on their principal. Holders in Non-Earning Mode do not accrue wM yield.

Sources of Excess Accumulation:

The excess $M balance primarily grows from:

  1. Yield on $M Backing Non-Earning wM (Primary Source): The $M yield generated by the portion of $M tokens backing the wM held by users in Non-Earning Mode directly contributes to the excess. Since these users aren't entitled to wM yield, the $M yield earned on their underlying share remains within the contract as surplus.
  2. Rounding Effects during Principal Conversions (Secondary Source & Potential Deficit): The contract utilizes principal-based accounting for earners, requiring conversions between token amounts and principal values whenever balances change (e.g., wrap, unwrap, startEarningFor, stopEarningFor, cross-status transfers).
  3. Direct $M Transfers: Any $M tokens sent directly to the WrappedMToken contract address (outside the wrap function) also become part of the excess.
  4. Cross-Status Transfers: The mechanics of transfers between earning and non-earning accounts can also contribute minor gains due to the specific rounding applied during the principal conversions involved.

Calculating Excess:

The contract calculates the available excess using the excess() view function:

Excess(wM contract)=M Balance(wM contract)(totalNonEarningSupply+ProjectedEarningSupply)Total wM Liabilities\text{Excess}_{(\text{wM contract})} = \text{M Balance}_{(\text{wM contract})} - \underbrace{\text{(totalNonEarningSupply} + \text{ProjectedEarningSupply)}}_{\text{Total wM Liabilities}}

Excess Management:

The accumulated excess $M tokens can be claimed and transferred out of the contract via the claimExcess() function. The excessDestination is set during deployment and typically points to the Distribution Vault, creating a revenue stream for the broader protocol ecosystem, ultimately benefiting Zero token holders.

Integration Considerations

For DeFi Protocols

When integrating with wM, protocols should be aware of:

  • Standard ERC-20 behavior (no rebasing)
  • Distinction between balance and balance-with-yield
  • Impact of earning status on user experience
  • Potential for yield to accumulate while tokens are locked

For Developers

Developers working with wM should understand:

  • The relationship between wM and the underlying $M token
  • The impact of earning status on token accounting
  • The claiming process and its effect on balances
  • The consistent rounding patterns used throughout the contract

Common Patterns

Several patterns are recommended when working with wM:

  • Check both balanceOf() and balanceWithYieldOf() to understand a user's position
  • Consider calling claimFor() before critical operations to ensure balances reflect all value
  • Be aware of the impact of earning status transitions on token amounts
  • Understand the priority order for claim recipients when handling claimed yield

Security Considerations

Trust Model

The wM system operates with several trust assumptions:

  • MigrationAdmin: Fully trusted role that can change contract implementation
  • EarnerManager MigrationAdmin: Can change EarnerManager implementation
  • Registrar: Source of truth for system parameters
  • MToken: Assumed to function correctly
  • Admins: Trusted to manage earning status appropriately

Edge Cases

Several edge cases are worth understanding:

Admin Removal

If an admin is removed from the admins list:

  • Accounts they approved lose earning status
  • Their fee settings become invalid
  • Accounts should be stopped from earning to prevent further yield accrual

Disabled Earning

If the wrapper contract is removed from M's earner list:

  • disableEarning() should be called to prevent further yield accrual
  • Individual accounts can continue to earn until that point

Balance Precision

The system has specific precision limitations:

  • All balances limited to uint240 to match $M token
  • Principal amounts limited to uint112 for gas efficiency
  • Indices use uint128 with 12 decimal places of precision

Rounding Effects

The consistent rounding strategy creates specific effects:

  • Multiple conversions between earning and non-earning states will result in small token losses
  • Transfers between accounts with different earning statuses create small protocol gains
  • These tiny amounts accumulate as protocol reserves, enhancing stability

Upgrades and Migration

Key Improvements in V2

Wrapped $M (wM) fundamentally operates as a non-rebasing token where yield is realized through manual claiming, a characteristic distinct from the native $M token's automatic rebasing. The V2 implementation builds upon this established model with several important enhancements:

  1. Re-enabling Earning: V2 allows earning to be re-enabled after being disabled
  2. Admin-Managed Earners: Introduction of EarnerManager for delegated administration
  3. Fee Mechanism: Ability for admins to take fees from yield
  4. Principal-Based Accounting: Changed from lastIndex tracking to principal amount tracking
  5. Explicit Claim Recipients: Added ability for users to explicitly set yield recipients
  6. Gas Efficiency: Optimized storage layout and operations

Migration from V1

The migration from V1 to V2 involved several key transformations:

Account Model Change

  • V1: Used last interaction index for earning accounts
  • V2: Uses principal amount for earning accounts
  • Migration formula: principal = balance / lastIndex

Index Handling

  • V1: Used an array for enable/disable indices
  • V2: Uses enableMIndex and disableIndex slots
  • Migration preserved earning state and index values

Migration Process

The migration was handled through a specialized migrator contract that:

  1. Identified all earning accounts in V1
  2. Converted their account structure to the V2 format
  3. Preserved their earning status and equivalent yield position
Copyright © M0 Foundation 2026