Skip to content

Minting LVMON

LVMONMinter converts native MON or WMON into LVMON. The current mainnet configuration mints LVMON 1:1 with the input amount; all three assets use 18 decimals.

This is a standalone helper contract, not a Diamond facet. Calls on this page go to the LVMONMinter address.

ContractAddress
LVMONMinter0x6FbEa6986F38aA85D09a8e9d8E5c71499ef70909
WMON0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A
LVMON0x91b81bfbe3A747230F0529Aa28d8b2Bc898E6D56

Access requirement

Only whitelisted callers can mint. Check the exact address that will call mint before asking the user to approve or send funds:

solidity
function whitelist(address account) external view returns (bool);

The check uses msg.sender. If a smart account or another contract submits the call, that contract must be whitelisted — whitelisting the user's EOA is not enough. Only the LVMONMinter owner can change this list; there is no public self-registration transaction.

ABI and setup

The examples use the clients and account from the Quickstart config.

ts
import { parseAbi } from 'viem'

export const LVMON_MINTER = '0x6FbEa6986F38aA85D09a8e9d8E5c71499ef70909' as const
export const WMON = '0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A' as const

export const LVMON_MINTER_ABI = parseAbi([
  'function whitelist(address account) view returns (bool)',
  'function mint(uint256 amountIn) payable returns (uint256 lvmonOut)',
  'function issuer() view returns (address)',
  'function wmon() view returns (address)',
  'function lvmon() view returns (address)',
  'event Minted(address indexed user, uint256 monIn, uint256 lvmonOut)',
])

Check access once at the start of the flow:

ts
import { account, publicClient } from './config'
import { LVMON_MINTER, LVMON_MINTER_ABI } from './lvmon-minter'

const allowed = await publicClient.readContract({
  address: LVMON_MINTER,
  abi: LVMON_MINTER_ABI,
  functionName: 'whitelist',
  args: [account.address],
})

if (!allowed) throw new Error('This caller is not authorized to mint LVMON')

Mint with native MON

Pass the same amount to the amountIn argument and transaction value. The contract wraps the MON to WMON, mints LVMON, and sends the LVMON back to msg.sender in one transaction.

ts
import { formatEther, parseEther, parseEventLogs } from 'viem'
import { account, publicClient, walletClient } from './config'
import { LVMON_MINTER, LVMON_MINTER_ABI } from './lvmon-minter'

const amountIn = parseEther('1')

const { request, result: expectedOut } = await publicClient.simulateContract({
  account,
  address: LVMON_MINTER,
  abi: LVMON_MINTER_ABI,
  functionName: 'mint',
  args: [amountIn],
  value: amountIn,
})

console.log('Expected LVMON:', formatEther(expectedOut))

const hash = await walletClient.writeContract(request)
const receipt = await publicClient.waitForTransactionReceipt({ hash })

const [minted] = parseEventLogs({
  abi: LVMON_MINTER_ABI,
  eventName: 'Minted',
  logs: receipt.logs,
})

if (!minted) throw new Error('Minted event not found')
console.log('Minted LVMON:', formatEther(minted.args.lvmonOut))

mint has no minLvmonOut parameter. Simulate immediately before submitting and use the returned value or Minted.lvmonOut as the authoritative output instead of assuming the conversion rate in accounting code.

Mint with WMON

For WMON, first approve the LVMONMinter, then call mint without transaction value.

ts
import { erc20Abi, parseEther } from 'viem'
import { account, publicClient, walletClient } from './config'
import { LVMON_MINTER, LVMON_MINTER_ABI, WMON } from './lvmon-minter'

const amountIn = parseEther('1')

const approvalHash = await walletClient.writeContract({
  account,
  address: WMON,
  abi: erc20Abi,
  functionName: 'approve',
  args: [LVMON_MINTER, amountIn],
})
await publicClient.waitForTransactionReceipt({ hash: approvalHash })

const { request } = await publicClient.simulateContract({
  account,
  address: LVMON_MINTER,
  abi: LVMON_MINTER_ABI,
  functionName: 'mint',
  args: [amountIn],
  // No value: sending any native MON selects the native-MON path.
})

const hash = await walletClient.writeContract(request)
await publicClient.waitForTransactionReceipt({ hash })

Approve only the amount you intend to mint. A successful call consumes that WMON allowance and delivers the resulting LVMON to the caller.

Function reference

solidity
function mint(uint256 amountIn) external payable returns (uint256 lvmonOut);
Input modeamountInmsg.valueAdditional requirement
Native MONMON amount, 18 decimalsMust equal amountInCaller needs enough MON for the input and gas
WMONWMON amount, 18 decimals0Approve the minter to spend at least amountIn WMON

The mode is selected solely by whether msg.value is greater than zero. Do not send native MON on a WMON mint call.

Read-only configuration:

FunctionReturns
whitelist(account)Whether account may call mint
issuer()The LVMON issuer used by the minter
wmon()The configured WMON token
lvmon()The LVMON token returned to the caller
owner()Address allowed to update the caller whitelist

setWhitelist(address account, bool allowed) also exists, but is owner-only. Integrations should use whitelist(account) to check access rather than attempting the administrative call.

Events

solidity
event Minted(address indexed user, uint256 monIn, uint256 lvmonOut);
event WhitelistUpdated(address indexed account, bool allowed);

Minted.monIn is the input amount for either mode: native MON or WMON. Minted.user is the caller and recipient of the LVMON.

Common reverts

RevertCause
lvmon-minter: caller not whitelistedmsg.sender is not on the minter's caller whitelist
lvmon-minter: amount mismatchNative msg.value does not equal amountIn
lvmon-minter: amount=0WMON mode was called with a zero amount
Token transfer failureWMON balance or allowance is below amountIn
mint not enabledProtocol-level LVMON minting is paused
token reserve disabledThe WMON reserve is disabled at the issuer

All wrapping, reserve deposit, minting, and transfer steps are atomic. If any step reverts, the caller keeps the original MON or WMON. A WMON approval submitted as an earlier transaction remains in place until it is used or replaced.

Trading perpetuals involves risk. Nothing here is financial advice.