Skip to content

WrappedMToken

Code

WrappedMToken is an ERC20 token for wrapping the M token into a non-rebasing token with claimable yields.

Token Overview

Account Struct

The WrappedMToken uses a special account struct to track the state of each user's balance:

struct Account {
    // First Slot
    bool isEarning;
    uint240 balance;
    // Second slot
    uint112 earningPrincipal;
    bool hasClaimRecipient;
    bool hasEarnerDetails;
}

This struct serves multiple purposes:

  • isEarning: Tracks whether the account is earning yield
  • balance: Represents the token balance held by the account
  • earningPrincipal: Tracks the principal amount for yield calculation
  • hasClaimRecipient: Indicates if account has a custom claim recipient
  • hasEarnerDetails: Indicates if account has additional earning parameters

Key State Variables

uint16 public constant HUNDRED_PERCENT = 10_000;
bytes32 public constant EARNERS_LIST_IGNORED_KEY = "earners_list_ignored";
bytes32 public constant EARNERS_LIST_NAME = "earners";
bytes32 public constant CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX = "wm_claim_override_recipient";
bytes32 public constant MIGRATOR_KEY_PREFIX = "wm_migrator_v2";
address public immutable earnerManager;
address public immutable migrationAdmin;
address public immutable mToken;
address public immutable registrar;
address public immutable excessDestination;
uint112 public totalEarningPrincipal;
uint240 public totalEarningSupply;
uint240 public totalNonEarningSupply;
mapping(address account => Account balance) internal _accounts;
uint128 public enableMIndex;
uint128 public disableIndex;
int240 public roundingError;
mapping(address account => address claimRecipient) internal _claimRecipients;

These variables track the essential state of the token system:

  • Constants for percentage calculations and registry keys
  • earnerManager: Address of the manager governing earning eligibility
  • migrationAdmin: Address allowed to perform migrations
  • mToken: Address of the underlying M token
  • registrar: Address of the Registrar for configuration
  • excessDestination: Address where excess tokens are sent
  • Supply tracking for earning and non-earning tokens
  • Index tracking for earning calculations
  • roundingError: Tracking imprecise M token transfers

Functions

constructor

constructor(
    address mToken_,
    address registrar_,
    address earnerManager_,
    address excessDestination_,
    address migrationAdmin_
) ERC20Extended("M (Wrapped) by M0", "wM", 6)

Initializes the token with:

  • Name: "M (Wrapped) by M0"
  • Symbol: "wM"
  • Decimals: 6
  • References to M Token, Registrar, Earner Manager, and other essential contracts

Parameters:

NameTypeDescription
mToken_addressThe M Token contract address
registrar_addressThe Registrar contract address
earnerManager_addressThe Earner Manager contract address
excessDestination_addressThe destination for excess tokens
migrationAdmin_addressThe migration admin address

wrap

function wrap(address recipient_, uint256 amount_) external

Wraps $M tokens into wrapped M tokens.

  • Transfers $M tokens from the caller to this contract
  • Mints wrapped $M tokens to the recipient

Parameters:

NameTypeDescription
recipient_addressThe recipient of the wrapped tokens
amount_uint256The amount of $M tokens to wrap

wrapWithPermit

function wrapWithPermit(
    address recipient_,
    uint256 amount_,
    uint256 deadline_,
    uint8 v_,
    bytes32 r_,
    bytes32 s_
) external

Wraps $M tokens using EIP-2612 permit for approval.

Parameters:

NameTypeDescription
recipient_addressThe recipient of the wrapped tokens
amount_uint256The amount of $M tokens to wrap
deadline_uint256Expiration timestamp for the permit
v_uint8ECDSA signature component
r_bytes32ECDSA signature component
s_bytes32ECDSA signature component

wrapWithPermit (alternative)

function wrapWithPermit(
    address recipient_,
    uint256 amount_,
    uint256 deadline_,
    bytes memory signature_
) external

Wraps $M tokens using arbitrary signature format.

Parameters:

NameTypeDescription
recipient_addressThe recipient of the wrapped tokens
amount_uint256The amount of $M tokens to wrap
deadline_uint256Expiration timestamp for the permit
signature_bytesArbitrary signature data

unwrap

function unwrap(address recipient_, uint256 amount_) external

Unwraps wrapped $M tokens back to M tokens.

  • Burns wrapped $M tokens from the caller
  • Transfers $M tokens to the recipient

Parameters:

NameTypeDescription
recipient_addressThe recipient of the unwrapped tokens
amount_uint256The amount of tokens to unwrap

claimFor

function claimFor(address account_) external returns (uint240 yield_)

Claims accrued yield for an account.

  • Only affects accounts in earning mode
  • Routes yield to configured claim recipient
  • Handles fee distribution if earner details exist

Parameters:

NameTypeDescription
account_addressThe account to claim yield for

Return Values:

NameTypeDescription
yield_uint240The amount of yield claimed

claimExcess

function claimExcess() external returns (uint240 claimed_)

Claims any excess $M tokens that aren't earmarked for balances.

  • Verifies excess exists before claiming
  • Transfers excess to the configured destination

Return Values:

NameTypeDescription
claimed_uint240The amount of excess tokens claimed

enableEarning

function enableEarning() external

Enables yield earning for the contract.

  • Verifies contract is approved as an earner
  • Records the current M index as the reference point
  • Calls startEarning on the M token

disableEarning

function disableEarning() external

Disables yield earning for the contract.

  • Verifies contract is not approved as an earner
  • Records the current index as the disable point
  • Calls stopEarning on the M token

startEarningFor

function startEarningFor(address account_) external

Transitions an account to earning mode.

  • Verifies earning is enabled globally
  • Checks if account is an approved earner
  • Updates account and global state variables

Parameters:

NameTypeDescription
account_addressThe account to start earning

startEarningFor (batch)

function startEarningFor(address[] calldata accounts_) external

Transitions multiple accounts to earning mode.

  • Processes each account individually

Parameters:

NameTypeDescription
accounts_address[]The accounts to start earning

stopEarningFor

function stopEarningFor(address account_) external

Transitions an account to non-earning mode.

  • Claims any accrued yield
  • Updates account and global state variables

Parameters:

NameTypeDescription
account_addressThe account to stop earning

stopEarningFor (batch)

function stopEarningFor(address[] calldata accounts_) external

Transitions multiple accounts to non-earning mode.

  • Processes each account individually

Parameters:

NameTypeDescription
accounts_address[]The accounts to stop earning

setClaimRecipient

function setClaimRecipient(address claimRecipient_) external

Sets a custom recipient for yield claims.

  • Updates the claim recipient mapping
  • Sets a flag on the account for optimization

Parameters:

NameTypeDescription
claimRecipient_addressThe recipient to receive yield

migrate

function migrate(address migrator_) external

Performs arbitrary migration logic.

  • Only callable by the migration admin
  • Delegates to migrator contract

Parameters:

NameTypeDescription
migrator_addressThe contract that will perform the migration

View/Pure Functions

accruedYieldOf

function accruedYieldOf(address account_) public view returns (uint240 yield_)

Returns the yield accrued by an account.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
yield_uint240The accrued yield for the account

balanceOf

function balanceOf(address account_) public view returns (uint256 balance_)

Returns the token balance of an account.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
balance_uint256Current token balance

balanceWithYieldOf

function balanceWithYieldOf(address account_) external view returns (uint256 balance_)

Returns the token balance plus accrued yield.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
balance_uint256Balance including accrued yield

earningPrincipalOf

function earningPrincipalOf(address account_) external view returns (uint112 earningPrincipal_)

Returns the principal amount for earning accounts.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
earningPrincipal_uint112Principal amount used for yield calculation

claimRecipientFor

function claimRecipientFor(address account_) public view returns (address recipient_)

Returns the recipient for an account's yield.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
recipient_addressAddress to receive the claimed yield

currentIndex

function currentIndex() public view returns (uint128 index_)

Returns the current index for yield calculations.

Return Values:

NameTypeDescription
index_uint128Current index value

isEarning

function isEarning(address account_) external view returns (bool isEarning_)

Checks if an account is in earning mode.

Parameters:

NameTypeDescription
account_addressThe account to check

Return Values:

NameTypeDescription
isEarning_boolWhether the account is earning yield

isEarningEnabled

function isEarningEnabled() public view returns (bool isEnabled_)

Checks if earning is enabled contract-wide.

Return Values:

NameTypeDescription
isEnabled_boolWhether global earning is enabled

excess

function excess() public view returns (int240 excess_)

Calculates excess $M tokens held by the contract.

Return Values:

NameTypeDescription
excess_int240Amount of excess $M tokens (can be negative)

totalAccruedYield

function totalAccruedYield() external view returns (uint240 yield_)

Calculates total yield accrued across all accounts.

Return Values:

NameTypeDescription
yield_uint240Total accrued yield

totalSupply

function totalSupply() external view returns (uint256 totalSupply_)

Returns the total supply of wrapped tokens.

Return Values:

NameTypeDescription
totalSupply_uint256Total supply of all tokens

projectedEarningSupply

function projectedEarningSupply() public view returns (uint240 supply_)

Calculates projected total of earning tokens including yield.

Return Values:

NameTypeDescription
supply_uint240Projected total of earning tokens

Key Internal Functions

_mint/_burn

Internal functions to mint and burn tokens, handling both earning and non-earning accounts.

_wrap/_unwrap

Internal functions to handle the wrapping and unwrapping of M tokens.

_claim

Internal function to calculate and distribute accrued yield.

_transfer

Internal override of the ERC20 transfer function handling transfers between different account types.

_startEarningFor/_stopEarningFor

Internal functions to handle transitioning accounts between earning and non-earning modes.

_getAccruedYield

Calculates the yield given an account's balance, earning principal, and current index.

_handleEarnerDetails

Calculates and transfers fees for accounts with earner details.

Events

event Claimed(address indexed account, address indexed recipient, uint240 yield);
event ClaimRecipientSet(address indexed account, address indexed claimRecipient);
event EarningEnabled(uint128 index);
event EarningDisabled(uint128 index);
event ExcessClaimed(uint240 excess);
event StartedEarning(address indexed account);
event StoppedEarning(address indexed account);
  • Claimed: Emitted when yield is claimed for an account
  • ClaimRecipientSet: Emitted when a claim recipient is set
  • EarningEnabled: Emitted when earning is enabled contract-wide
  • EarningDisabled: Emitted when earning is disabled contract-wide
  • ExcessClaimed: Emitted when excess tokens are claimed
  • StartedEarning: Emitted when an account starts earning
  • StoppedEarning: Emitted when an account stops earning

Custom Errors

error EarningIsDisabled();
error EarningIsEnabled();
error IsApprovedEarner(address account);
error InsufficientBalance(address account, uint240 balance, uint240 amount);
error NotApprovedEarner(address account);
error NoExcess();
error UnauthorizedMigration();
error ZeroEarnerManager();
error ZeroExcessDestination();
error ZeroMToken();
error ZeroMigrationAdmin();
error ZeroRegistrar();

These custom errors provide clear signals about issues during operations.