Wrapped M (wM)
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:
- Maintaining fixed token balances that don't automatically increase
- Separately tracking and allowing manual claiming of accrued yield
- Preserving the full economic benefits of the M0 platform while improving protocol compatibility
- 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
$Mtoken - Value & Solvency: wM aims for a direct value link to
$Mfor earners. The target value for an earning token is its underlying$Mprincipal plus accrued yield:
Critically, the entire system maintains a solvency invariant, ensuring it's always fully backed. The total$Mheld by the wM contract is always greater than or equal to the total wM supply plus all accrued yield owed to earners:
This guarantee is maintained through the system's design, where yield earned on$Mheld 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 | $M | Wrapped M |
|---|---|---|
| Balance Behavior | Automatically increases (rebasing) | Remains static until explicit claim |
| Earning Mechanism | Implicit (balances grow) | Explicit (yield accumulates separately) |
| Yield Realization | Automatic with balance updates | Manual claiming required |
| DeFi Compatibility | Limited due to rebasing | Wide compatibility with standard ERC-20 |
| Recipient Options | Limited | Configurable yield recipients |
| Administration | Governance only | Governance + 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
$Mtoken. - Crucially, the index used is derived from the underlying
$Mtoken (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:
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:
So, the target state before claiming is effectively:
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
$Mtoken's index) - The time elapsed since the last index update
The formula is:
Where:
principalis the stored earning principal for the accountcurrentIndexis the current wM indexbalanceis the current token balance
Claiming Process
When yield is claimed:
- The accrued yield is calculated based on the current index
- The account's balance is provisionally increased by the full yield amount
- 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
- 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:
Where:
M current indexis the current index from the$MtokenenableMIndexis the$Mindex when earning was enableddisableIndexis the previous wM index (if earning was previously disabled)
When Earning is Disabled
The wM index becomes static:
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:
- User-specified recipient (if set)
- Governance-specified override (if set)
- 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:
Expressed using contract variables:
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:
- The yield generated by the underlying
$Mtokens that back the balances of non-earning wM holders (as this yield isn't owed back to them). - 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
earningPrincipaland the time elapsed (represented by the increase in thecurrentIndex) 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.
- Returns the amount of wM tokens earned as yield based on the account's
balanceWithYieldOf(account):- Returns the sum of the account's current stored
balanceOfplus the currently claimableaccruedYieldOf. - 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).
- Returns the sum of the account's current stored
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 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 balancestotalEarningSupply: 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:
- wM Contract Earns
$MYield: As confirmed by its status as a major$Mholder and its requirement to be an approved M earner (enableEarningrequires_isThisApprovedEarner), the entire balance of$Mtokens held within theWrappedMTokencontract continuously earns yield according to the$Mtoken's native rebasing mechanism. - 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:
- Yield on
$MBacking Non-Earning wM (Primary Source): The$Myield generated by the portion of$Mtokens 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$Myield earned on their underlying share remains within the contract as surplus. - 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). - Direct
$MTransfers: Any$Mtokens sent directly to theWrappedMTokencontract address (outside thewrapfunction) also become part of the excess. - 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 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
$Mtoken - 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()andbalanceWithYieldOf()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
$Mtoken - 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:
- Re-enabling Earning: V2 allows earning to be re-enabled after being disabled
- Admin-Managed Earners: Introduction of EarnerManager for delegated administration
- Fee Mechanism: Ability for admins to take fees from yield
- Principal-Based Accounting: Changed from lastIndex tracking to principal amount tracking
- Explicit Claim Recipients: Added ability for users to explicitly set yield recipients
- 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:
- Identified all earning accounts in V1
- Converted their account structure to the V2 format
- Preserved their earning status and equivalent yield position
M0 On Solana
Technical deep dive into the M0 Protocol's implementation on the Solana blockchain, including program specifications, V2 upgrades, and developer integration guides.
Wrapped M Specification
Low-level technical specification for Wrapped M (wM) - function signatures, events, errors, and storage layout.