Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Multi-Collateral Option: JMIExtension

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

When to Use JMI instead of standard MYieldToOne:
  • Projects requiring flexibility to accept multiple stablecoin 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 yield is centralized but collateral sources are diversified

Source Code: JMIExtension.sol

Key Differentiators from MYieldToOne

FeatureMYieldToOne (Standard)JMIExtension (JMI)
CollateralSingle-collateralMulti-collateral (USDC, USDT, etc.)
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
  • Yield 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 either $M or any approved stablecoin (USDC, DAI, etc.) through the SwapFacility. The contract tracks each asset's balance separately.

  2. Asset Caps: Each non-$M collateral has a maximum cap. This limits risk exposure to any single stablecoin. The $M token has no cap and can always be wrapped.

  3. Yield Accrual: Only the $M portion of the backing earns yield. As the contract's $M balance grows beyond its required backing, yield becomes claimable.

  4. Yield Distribution: The claimYield() function mints new JMI tokens to the designated yieldRecipient, equal to the yield surplus.

  5. Selective Unwrapping: Users can only unwrap to $M. The maximum unwrap amount is limited to the $M backing (total supply minus non-$M asset backing).

  6. Asset Replacement: The replaceAssetWithM function allows swapping $M for any non-$M collateral 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 non-$M collateral (in extension decimals)

The claimable yield is calculated as:

Yield = mBalanceOf(contract) - M Backing

The JMI backing model assumes a 1:1 peg between all deposited assets and $M. 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 non-$M collateral asset via setAssetCap().
YIELD_RECIPIENT_MANAGER_ROLEControls where yield is 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 a non-$M collateral asset. Called via SwapFacility.
wrap(recipient, amount)Mints JMI tokens by depositing $M (inherited from MExtension). Called via SwapFacility.

Unwrapping Functions

FunctionDescription
unwrap(recipient, amount)Burns JMI tokens and sends $M to the recipient. Limited to $M backing.

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 $M for non-$M collateral held by the contract.

Yield Functions (Inherited)

FunctionDescription
yield()Returns the current claimable yield amount.
claimYield()Claims yield by minting new JMI tokens to the yield recipient.
setYieldRecipient(address)Updates the yield 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 non-$M backing (in extension decimals)
isAllowedAsset(asset)boolTrue if asset is $M 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 for $M
YieldClaimedamountYield was claimed
YieldRecipientSetyieldRecipientYield 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 $M backing for unwrap
ZeroAssetCapManager()Asset cap manager is zero address
ZeroAdmin()Admin is zero address
ZeroYieldRecipient()Yield recipient is zero address
ZeroYieldRecipientManager()Yield 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 M Backing: Users cannot unwrap more JMI than the contract's $M 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 →