🪙mTokens
mTokens are ERC-20 compliant tokens that represent positions within the Moonwell protocol. When you supply assets, you receive mTokens in return. As interest accrues to the market, the exchange rate between mTokens and the underlying asset increases - meaning your mTokens become redeemable for a growing amount of the underlying over time.
mTokens are transferrable and fungible, and can be redeemed for the underlying asset provided there is sufficient liquidity and the position has not been designated as collateral.
Each market supported by the protocol (e.g. USDC, WETH, cbBTC) is represented by its own mToken contract. The mToken is responsible for:
Accepting new deposits (minting mTokens)
Allowing withdrawals of assets from the protocol (redeeming/burning mTokens)
Managing borrow requests for a specific asset
Facilitating repayments for a specific market
Handling liquidations for specific market positions
Calculating reserve and seize shares to increase protocol reserves
Integration Quickstart
The following example demonstrates a complete supply → borrow → repay → withdraw flow. All interactions go through the mToken contracts directly. Before borrowing, you must enter the market via the Comptroller.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {MErc20} from "@moonwell/MErc20.sol";
import {EIP20Interface} from "@moonwell/EIP20Interface.sol";
import {ComptrollerInterface} from "@moonwell/ComptrollerInterface.sol";
contract MoonwellIntegration {
/// @notice Supply USDC and borrow WETH against it
function supplyAndBorrow(
address mUSDC, // Moonwell USDC mToken
address mWETH, // Moonwell WETH mToken
address comptroller,
uint256 supplyAmount,
uint256 borrowAmount
) external {
// 1. Approve the mToken to pull underlying
EIP20Interface usdc = EIP20Interface(MErc20(mUSDC).underlying());
usdc.approve(mUSDC, supplyAmount);
// 2. Supply - mints mTokens to this contract
uint err = MErc20(mUSDC).mint(supplyAmount);
require(err == 0, "mint failed");
// 3. Enter the market - enables USDC as collateral
address[] memory markets = new address[](1);
markets[0] = mUSDC;
uint[] memory results = ComptrollerInterface(comptroller).enterMarkets(markets);
require(results[0] == 0, "enterMarkets failed");
// 4. Borrow WETH against USDC collateral
err = MErc20(mWETH).borrow(borrowAmount);
require(err == 0, "borrow failed");
}
/// @notice Repay WETH borrow and withdraw USDC
function repayAndWithdraw(
address mUSDC,
address mWETH,
uint256 repayAmount,
uint256 withdrawAmount
) external {
// 1. Approve and repay the borrow
EIP20Interface weth = EIP20Interface(MErc20(mWETH).underlying());
weth.approve(mWETH, repayAmount);
uint err = MErc20(mWETH).repayBorrow(repayAmount);
require(err == 0, "repay failed");
// 2. Withdraw USDC by specifying the underlying amount
err = MErc20(mUSDC).redeemUnderlying(withdrawAmount);
require(err == 0, "redeem failed");
}
/// @notice Query current positions
function getPositions(
address mToken,
address account
) external view returns (
uint256 mTokenBalance,
uint256 borrowBalance,
uint256 exchangeRate
) {
(uint err, uint bal, uint borrows, uint rate) =
MErc20(mToken).getAccountSnapshot(account);
require(err == 0, "snapshot failed");
return (bal, borrows, rate);
}
}Key Concepts
Exchange Rate
The exchange rate determines how many underlying tokens each mToken is worth:
As interest accrues, totalBorrows grows, increasing the exchange rate. This means suppliers earn interest simply by holding mTokens - no claiming required.
Interest Accrual
Interest accrues per-second (not per-block). Rates are determined by the market's interest rate model based on utilization:
Higher utilization means higher borrow rates and higher supply rates.
Error Handling
mToken functions use two failure modes:
Error codes - Business logic failures (comptroller rejection, insufficient collateral, freshness checks) return a non-zero
uintwithout reverting. Always check the return value.Reverts - Underlying token transfer failures, math overflows, and Comptroller pause/cap enforcement use
require()and revert the transaction.
Integrators should handle both: use try/catch for reverts, and check return values for error codes. See Contract Interactions for the full error code table.
Deployed Contracts
Base
Comptroller
Moonwell USDC
Moonwell WETH
Moonwell cbETH
Moonwell wstETH
Moonwell cbBTC
Moonwell DAI
Moonwell AERO
Moonwell EURC
OP Mainnet
Comptroller
Moonwell USDC
Moonwell WETH
Moonwell USDT
Moonwell WBTC
Moonwell wstETH
Moonwell OP
For the complete list of all deployed contracts across all chains, see Contracts.
📝Contract InteractionsLast updated

