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 yieldbalance
: Represents the token balance held by the accountearningPrincipal
: Tracks the principal amount for yield calculationhasClaimRecipient
: Indicates if account has a custom claim recipienthasEarnerDetails
: 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 eligibilitymigrationAdmin
: Address allowed to perform migrationsmToken
: Address of the underlying M tokenregistrar
: Address of the Registrar for configurationexcessDestination
: 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:
Name | Type | Description |
---|---|---|
mToken_ | address | The M Token contract address |
registrar_ | address | The Registrar contract address |
earnerManager_ | address | The Earner Manager contract address |
excessDestination_ | address | The destination for excess tokens |
migrationAdmin_ | address | The 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:
Name | Type | Description |
---|---|---|
recipient_ | address | The recipient of the wrapped tokens |
amount_ | uint256 | The 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:
Name | Type | Description |
---|---|---|
recipient_ | address | The recipient of the wrapped tokens |
amount_ | uint256 | The amount of $M tokens to wrap |
deadline_ | uint256 | Expiration timestamp for the permit |
v_ | uint8 | ECDSA signature component |
r_ | bytes32 | ECDSA signature component |
s_ | bytes32 | ECDSA signature component |
wrapWithPermit (alternative)
function wrapWithPermit(
address recipient_,
uint256 amount_,
uint256 deadline_,
bytes memory signature_
) external
Wraps $M tokens using arbitrary signature format.
Parameters:
Name | Type | Description |
---|---|---|
recipient_ | address | The recipient of the wrapped tokens |
amount_ | uint256 | The amount of $M tokens to wrap |
deadline_ | uint256 | Expiration timestamp for the permit |
signature_ | bytes | Arbitrary 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:
Name | Type | Description |
---|---|---|
recipient_ | address | The recipient of the unwrapped tokens |
amount_ | uint256 | The 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:
Name | Type | Description |
---|---|---|
account_ | address | The account to claim yield for |
Return Values:
Name | Type | Description |
---|---|---|
yield_ | uint240 | The 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:
Name | Type | Description |
---|---|---|
claimed_ | uint240 | The 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:
Name | Type | Description |
---|---|---|
account_ | address | The account to start earning |
startEarningFor (batch)
function startEarningFor(address[] calldata accounts_) external
Transitions multiple accounts to earning mode.
- Processes each account individually
Parameters:
Name | Type | Description |
---|---|---|
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:
Name | Type | Description |
---|---|---|
account_ | address | The account to stop earning |
stopEarningFor (batch)
function stopEarningFor(address[] calldata accounts_) external
Transitions multiple accounts to non-earning mode.
- Processes each account individually
Parameters:
Name | Type | Description |
---|---|---|
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:
Name | Type | Description |
---|---|---|
claimRecipient_ | address | The 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:
Name | Type | Description |
---|---|---|
migrator_ | address | The 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:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
yield_ | uint240 | The accrued yield for the account |
balanceOf
function balanceOf(address account_) public view returns (uint256 balance_)
Returns the token balance of an account.
Parameters:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
balance_ | uint256 | Current token balance |
balanceWithYieldOf
function balanceWithYieldOf(address account_) external view returns (uint256 balance_)
Returns the token balance plus accrued yield.
Parameters:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
balance_ | uint256 | Balance including accrued yield |
earningPrincipalOf
function earningPrincipalOf(address account_) external view returns (uint112 earningPrincipal_)
Returns the principal amount for earning accounts.
Parameters:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
earningPrincipal_ | uint112 | Principal amount used for yield calculation |
claimRecipientFor
function claimRecipientFor(address account_) public view returns (address recipient_)
Returns the recipient for an account's yield.
Parameters:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
recipient_ | address | Address to receive the claimed yield |
currentIndex
function currentIndex() public view returns (uint128 index_)
Returns the current index for yield calculations.
Return Values:
Name | Type | Description |
---|---|---|
index_ | uint128 | Current index value |
isEarning
function isEarning(address account_) external view returns (bool isEarning_)
Checks if an account is in earning mode.
Parameters:
Name | Type | Description |
---|---|---|
account_ | address | The account to check |
Return Values:
Name | Type | Description |
---|---|---|
isEarning_ | bool | Whether the account is earning yield |
isEarningEnabled
function isEarningEnabled() public view returns (bool isEnabled_)
Checks if earning is enabled contract-wide.
Return Values:
Name | Type | Description |
---|---|---|
isEnabled_ | bool | Whether global earning is enabled |
excess
function excess() public view returns (int240 excess_)
Calculates excess $M tokens held by the contract.
Return Values:
Name | Type | Description |
---|---|---|
excess_ | int240 | Amount of excess $M tokens (can be negative) |
totalAccruedYield
function totalAccruedYield() external view returns (uint240 yield_)
Calculates total yield accrued across all accounts.
Return Values:
Name | Type | Description |
---|---|---|
yield_ | uint240 | Total accrued yield |
totalSupply
function totalSupply() external view returns (uint256 totalSupply_)
Returns the total supply of wrapped tokens.
Return Values:
Name | Type | Description |
---|---|---|
totalSupply_ | uint256 | Total supply of all tokens |
projectedEarningSupply
function projectedEarningSupply() public view returns (uint240 supply_)
Calculates projected total of earning tokens including yield.
Return Values:
Name | Type | Description |
---|---|---|
supply_ | uint240 | Projected 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 accountClaimRecipientSet
: Emitted when a claim recipient is setEarningEnabled
: Emitted when earning is enabled contract-wideEarningDisabled
: Emitted when earning is disabled contract-wideExcessClaimed
: Emitted when excess tokens are claimedStartedEarning
: Emitted when an account starts earningStoppedEarning
: 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.