# 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.

| Contract | Address |
| :--- | :--- |
| LVMONMinter | `0x6FbEa6986F38aA85D09a8e9d8E5c71499ef70909` |
| WMON | `0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A` |
| LVMON | `0x91b81bfbe3A747230F0529Aa28d8b2Bc898E6D56` |

## 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](/introduction/quickstart#2-shared-setup).

```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 mode | `amountIn` | `msg.value` | Additional requirement |
| :--- | ---: | ---: | :--- |
| Native MON | MON amount, 18 decimals | Must equal `amountIn` | Caller needs enough MON for the input and gas |
| WMON | WMON amount, 18 decimals | `0` | Approve 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:

| Function | Returns |
| :--- | :--- |
| `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

| Revert | Cause |
| :--- | :--- |
| `lvmon-minter: caller not whitelisted` | `msg.sender` is not on the minter's caller whitelist |
| `lvmon-minter: amount mismatch` | Native `msg.value` does not equal `amountIn` |
| `lvmon-minter: amount=0` | WMON mode was called with a zero amount |
| Token transfer failure | WMON balance or allowance is below `amountIn` |
| `mint not enabled` | Protocol-level LVMON minting is paused |
| `token reserve disabled` | The 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.
