Technical Specifications

Wrapped M Specification

Low-level technical specification for Wrapped M (wM) - function signatures, events, errors, and storage layout.

Contract overview

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

  • Decimals: 6 (matches $M)
  • Index: Derived from the underlying $M token's index
  • Yield realization: Explicit claiming (not automatic rebasing)

Key functions

Wrapping and Unwrapping

FunctionDescription
wrap(address recipient, uint256 amount)Deposit $M tokens, receive equivalent wM
unwrap(address recipient, uint256 amount)Burn wM tokens, receive equivalent $M
wrapWithPermit(address recipient, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)Wrap with gasless $M approval

ERC20 Standard

FunctionDescription
transfer(address to, uint256 amount)Transfer wM tokens
transferFrom(address from, address to, uint256 amount)Transfer wM on behalf of another address
approve(address spender, uint256 amount)Approve a spender allowance
balanceOf(address account)Returns stored balance (excludes unclaimed yield)
totalSupply()Returns total wM in circulation

Earning Management

FunctionDescription
startEarningFor(address account)Activate earning mode for an approved account
stopEarningFor(address account)Deactivate earning mode (claims outstanding yield)
enableEarning()Enable earning for the wM contract itself on $M
disableEarning()Disable earning for the wM contract on $M

Yield Management

FunctionDescription
claimFor(address account)Claim accrued yield for an account
setClaimRecipient(address recipient)Set a custom address to receive claimed yield

Balance and Yield Queries

FunctionReturns
balanceOf(address account)Stored balance (excludes unclaimed yield)
accruedYieldOf(address account)Unclaimed yield for an earning account
balanceWithYieldOf(address account)balanceOf + accruedYieldOf
isEarning(address account)Whether the account is in earning mode
totalNonEarningSupply()Sum of all non-earning balances
totalEarningSupply()Sum of all earning balances (excluding yield)
projectedEarningSupply()Total earning supply if all yield were claimed
excess()$M held by contract minus total wM liabilities

Excess Management

FunctionDescription
claimExcess()Transfer accumulated excess $M to excessDestination
excessDestination()Returns the address that receives claimed excess (typically Distribution Vault)

Admin Operations (via EarnerManager)

FunctionDescription
setEarnerDetails(address account, bool status, uint16 feeRate)Set earning status and fee rate for an account
setEarnerDetails(address[] accounts, bool[] statuses, uint16[] feeRates)Bulk set earning details
earnerStatusFor(address account)Check earner status for an account
getEarnerDetails(address account)Get full earner details including admin and fee rate

Account storage model

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
}

Events

EventDescription
Transfer(address indexed from, address indexed to, uint256 value)Standard ERC20 transfer
Approval(address indexed owner, address indexed spender, uint256 value)Standard ERC20 approval
StartedEarning(address indexed account)Account entered earning mode
StoppedEarning(address indexed account)Account exited earning mode
Claimed(address indexed account, address indexed recipient, uint256 yield)Yield claimed
ExcessClaimed(uint240 amount)Excess $M claimed to destination

Errors

ErrorCondition
NotApprovedEarner()Account is not approved for earning
EarningIsEnabled()Operation not permitted while earning is enabled
EarningIsDisabled()Operation requires earning to be enabled
InsufficientBalance(address account, uint256 balance, uint256 amount)Transfer, unwrap, or burn exceeds balance

Index mechanism

The wM index is derived from the underlying $M token's index:

When earning is enabled:

wMindex=McurrentIndexenableMIndex×disableIndexwM_{\text{index}} = \frac{M_{\text{currentIndex}}}{enableMIndex} \times disableIndex

When earning is disabled:

wMindex=disableIndexwM_{\text{index}} = disableIndex

Yield claim recipient priority

When determining where to send claimed yield:

  1. User-specified recipient (via setClaimRecipient)
  2. Governance-specified override (via Registrar)
  3. The account itself (default)

Solvency invariant

M Balance(wM contract)totalNonEarningSupply+projectedEarningSupply\text{M Balance}_{(\text{wM contract})} \ge \text{totalNonEarningSupply} + \text{projectedEarningSupply}

The excess above this threshold accumulates from yield on $M backing non-earning wM holders and minor rounding effects.


Copyright © M0 Foundation 2026