Design Your Stablecoin

Getting started

Deep dive into the JMI ("Just Mint It") extension template which accepts multiple collateral types while directing 100% of rewards to a single recipient.
The JMI extension is currently available on EVM chains only (Ethereum, Base, Arbitrum, etc.). SVM support is planned for a future release.

The JMIExtension (Just Mint It) is an enhanced Treasury Model implementation that accepts multiple collateral types while still directing 100% of rewards to a single recipient.

When to Use JMI instead of standard MYieldToOne:

  • Projects requiring flexibility to accept multiple stablecoins as collateral types (USDC, USDT, DAI).
  • Protocols wanting a non-rebasing stablecoin backed by a diversified collateral pool.
  • Ecosystems that need to manage risk exposure to different stablecoin assets through configurable caps.
  • Treasury management scenarios where rewards are centralized but collateral sources are diversified.

Source Code: JMIExtension.sol

Key Differentiators from MYieldToOne

FeatureMYieldToOne (Standard)JMIExtension (JMI)
CollateralSingle-collateralMulti-collateral (liquid stablecoins such as USDC, USDT also supported)
Asset CapsN/AConfigurable per asset
Unwrap LogicAlways backed 1:1Limited to base backing portion
Additional OperationsNonereplaceAssetWithM

Architecture Overview

JMIExtension inherits from MYieldToOne, which itself inherits from MExtension, Freezable, and Pausable. This inheritance chain provides:

  • ERC-20 Token Logic via MExtension
  • Rewards Distribution via MYieldToOne (100% to single recipient)
  • Compliance Controls via Freezable (address-level restrictions)
  • Emergency Controls via Pausable (contract-wide halt)
  • Multi-Collateral Logic via JMIExtension (asset caps, multiple collateral types)
JMIExtension
├── IJMIExtension (interface)
├── JMIExtensionLayout (storage)
└── MYieldToOne
    ├── IMYieldToOne (interface)
    ├── MYieldToOneStorageLayout (storage)
    ├── MExtension (core ERC-20 + wrap/unwrap)
    ├── Freezable (address freezing)
    └── Pausable (emergency stop)

How It Works

  1. Multi-Collateral Wrapping: Users can wrap an approved stablecoin (USDC, DAI, etc.) through the SwapFacility. The contract tracks each asset's balance separately.
  2. Asset Caps: Each stablecoin collateral component has a maximum cap. This limits risk exposure to any single stablecoin.
  3. Rewards Accrual: Only the non-stablecoin collateral portion of the reserves earns rewards.
  4. Rewards Distribution: The claimYield() function mints new JMI tokens to the designated yieldRecipient, equal to the rewards surplus.
  5. Selective Unwrapping: Users can only unwrap to the base asset. The maximum unwrap amount is limited to the non-stablecoin collateral portion of reserves (total supply minus stablecoin asset backing).
  6. Asset Replacement: The replaceAssetWithM function allows swapping approved collateral for approved stablecoins held by the contract, enabling arbitrageurs to rebalance the backing.

Backing Model

The total supply of JMI tokens is backed by two components:

Total Supply = M Backing + Total Non-M Assets

Where:

  • M Backing = totalSupply() - totalAssets()
  • Total Non-M Assets = Sum of all approved stablecoin collateral (in extension decimals)

The claimable rewards are calculated as:

Rewards = mBalanceOf(contract) - M Backing

The JMI backing model assumes a 1:1 peg between all deposited assets. This means 1 USDC deposited equals 1 JMI token minted, and 1 DAI deposited equals 1 JMI token minted (accounting for decimal conversions).

Roles (Access Control)

JMIExtension uses OpenZeppelin's AccessControl with these roles:

RolePermissions
DEFAULT_ADMIN_ROLESuper-user. Can grant and revoke any role. Should be a secure multi-sig or governance contract.
ASSET_CAP_MANAGER_ROLEManages risk by setting caps on each approved stablecoin collateral asset via setAssetCap().
YIELD_RECIPIENT_MANAGER_ROLEControls where rewards are distributed via setYieldRecipient().
FREEZE_MANAGER_ROLECan freeze/unfreeze addresses from interacting with the token.
PAUSER_ROLECan pause/unpause the entire contract in emergencies.

Core Functions

Wrapping Functions

FunctionDescription
wrap(asset, recipient, amount)Mints JMI tokens by depositing an approved stablecoin collateral asset. Called via SwapFacility.
wrap(recipient, amount)Mints JMI tokens by depositing approved collateral (inherited from MExtension). Called via SwapFacility.

Unwrapping Functions

FunctionDescription
unwrap(recipient, amount)Burns JMI tokens and sends approved collateral to the recipient.

Asset Management Functions

FunctionAccessDescription
setAssetCap(asset, cap)ASSET_CAP_MANAGER_ROLESets the maximum balance allowed for a collateral asset.
replaceAssetWithM(asset, recipient, amount)SwapFacility onlySwaps approved collateral for approved stablecoins held by the contract.

Rewards Functions (Inherited)

FunctionDescription
yield()Returns the current claimable rewards amount.
claimYield()Claims rewards by minting new JMI tokens to the recipient.
setYieldRecipient(address)Updates the rewards recipient address.

View Functions

FunctionReturnsDescription
assetBalanceOf(asset)uint256Tracked balance of a collateral asset
assetCap(asset)uint256Cap for a collateral asset (0 = not allowed)
assetDecimals(asset)uint8Cached decimals for a collateral asset
totalAssets()uint256Total approved stablecoin backing (in extension decimals)
isAllowedAsset(asset)boolTrue if asset is approved collateral or has non-zero cap
isAllowedToWrap(asset, amount)boolTrue if wrap would succeed
isAllowedToUnwrap(amount)boolTrue if unwrap would succeed
isAllowedToReplaceAssetWithM(asset, amount)boolTrue if asset balance >= amount

Freezing & Pause Functions (Inherited)

FunctionAccessDescription
freeze(account)FREEZE_MANAGER_ROLEFreezes an address
unfreeze(account)FREEZE_MANAGER_ROLEUnfreezes an address
freezeAccounts(accounts[])FREEZE_MANAGER_ROLEBatch freeze
unfreezeAccounts(accounts[])FREEZE_MANAGER_ROLEBatch unfreeze
isFrozen(account)PublicCheck if address is frozen
pause()PAUSER_ROLEPauses the contract
unpause()PAUSER_ROLEUnpauses the contract
paused()PublicCheck if contract is paused

Events

EventParametersDescription
AssetCapSetasset, capAsset cap was updated
AssetReplacedWithMasset, assetAmount, recipient, mAmountCollateral swapped
YieldClaimedamountRewards claimed
YieldRecipientSetyieldRecipientRewards recipient changed
Transferfrom, to, amountERC-20 transfer
Approvalowner, spender, amountERC-20 approval
Frozenaccount, timestampAccount was frozen
Unfrozenaccount, timestampAccount was unfrozen
PausedaccountContract was paused
UnpausedaccountContract was unpaused

Errors

ErrorDescription
InvalidAsset(asset)Asset is address(0) or $M (for multi-asset functions)
AssetCapReached(asset)Deposit would exceed asset cap
InsufficientAssetBacking(asset, required, available)Not enough collateral for replacement
InsufficientAssetReceived(asset, expected, received)Fee-on-transfer token detected
InsufficientMBacking(required, available)Not enough approved collateral backing for unwrap
ZeroAssetCapManager()Asset cap manager is zero address
ZeroAdmin()Admin is zero address
ZeroYieldRecipient()Rewards recipient is zero address
ZeroYieldRecipientManager()Rewards recipient manager is zero address
ZeroFreezeManager()Freeze manager is zero address
ZeroPauser()Pauser is zero address
AccountFrozen(account)Operation blocked due to frozen account
EnforcedPause()Operation blocked due to paused state

Important Constraints

  • Fee-on-Transfer Tokens: Not supported. The contract reverts if fewer tokens are received than expected.
  • Only 1:1 Pegged Stablecoins: The model assumes all accepted collateral is pegged 1:1 to the dollar.
  • Unwrap Limited to Approved Collateral Backing: Users cannot unwrap more JMI than the contract's approved collateral backing supports.

Security Considerations

  1. Fee-on-Transfer Protection: The contract explicitly checks received amounts match expected amounts, reverting on discrepancies.
  2. Inflation Attack Prevention: The replaceAssetWithM function uses the tracked assetBalanceOf rather than actual token balance, preventing manipulation via direct token transfers.
  3. Access Control: All sensitive functions are protected by role-based access control.
  4. Emergency Controls: The PAUSER_ROLE can halt operations in case of security incidents.
  5. Frozen Accounts: The FREEZE_MANAGER_ROLE can block compromised addresses from interacting with the token.
  6. Upgradability: The contract uses OpenZeppelin's transparent proxy pattern, allowing for future upgrades with proper governance.

Ready to build? Follow the JMI implementation guide

Copyright © M0 Foundation 2026