LP Farming
LP Farming allows liquidity providers to earn additional reward tokens by staking their MonoSwap LP tokens. The LPFarming contract follows the MasterChef model with one critical difference: rewards come from a pre-funded vault rather than token minting. This means the reward supply is finite and transparent.
How It Works
+-------------------+ +-------------------+ +-------------------+
| You provide | | You deposit LP | | You earn reward |
| liquidity on | ----> | tokens into the | ----> | tokens over time |
| MonoSwap | | LPFarming contract| | (claim anytime) |
+-------------------+ +-------------------+ +-------------------+
Receive MONO-LP Stake MONO-LP Rewards accrue
tokens tokens per second
Pre-Funded Vault Model
Unlike protocols that mint new tokens as rewards (creating inflation), LPFarming distributes tokens that are pre-deposited into the contract:
1. Owner deposits reward tokens into LPFarming contract
2. Owner sets rewardPerSecond rate
3. Users stake LP tokens and earn proportional rewards
4. When the vault runs out, rewards stop (no minting)
This model provides transparency -- anyone can check how many reward tokens remain and calculate the runway:
function rewardHealth() external view returns (
uint256 available, // Reward tokens in contract
uint256 emittingPerDay, // Rewards emitted per day
uint256 runwaySeconds // Seconds of rewards remaining
);
Pool Structure
Each farming pool is defined by:
| Field | Type | Description |
|---|---|---|
lpToken | address | The MonoSwap LP token to stake |
allocPoint | uint256 | Pool's share of total rewards |
lastRewardTime | uint256 | Last time rewards were calculated |
accRewardPerShare | uint256 | Accumulated rewards per LP token (scaled by 1e18) |
Multiple pools can exist simultaneously, each for a different LP token (e.g., TOKEN_A/WLYTH LP, TOKEN_B/WLYTH LP). No duplicate pools are allowed for the same LP token.
Allocation Points
Rewards are distributed across pools based on their allocation points relative to the total:
Pool reward rate = rewardPerSecond * pool.allocPoint / totalAllocPoint
Example:
rewardPerSecond = 1 LYTH/sec
Pool A: allocPoint = 300 (300/500 = 60% of rewards)
Pool B: allocPoint = 200 (200/500 = 40% of rewards)
totalAllocPoint = 500
Pool A earns: 0.6 LYTH/sec
Pool B earns: 0.4 LYTH/sec
The contract owner can adjust allocation points at any time via set(pid, newAllocPoint). Setting a pool's allocation to 0 effectively disables rewards for that pool without preventing withdrawals.
Reward Calculation
Rewards accrue continuously based on time elapsed and the user's share of the pool.
Per-Pool Accumulator
Every time a pool is updated (on any deposit, withdrawal, or manual update), the accumulated rewards per share are recalculated:
elapsed = currentTime - pool.lastRewardTime
poolReward = elapsed * rewardPerSecond * pool.allocPoint / totalAllocPoint
accRewardPerShare += poolReward * 1e18 / lpSupply
Per-User Rewards
Each user's pending reward is calculated as:
pending = (user.amount * pool.accRewardPerShare / 1e18) - user.rewardDebt
The rewardDebt is updated on every deposit and withdrawal to ensure users only earn rewards for the period they are staked:
After deposit or withdrawal:
user.rewardDebt = user.amount * pool.accRewardPerShare / 1e18
Worked Example
Setup:
rewardPerSecond = 10 tokens/sec
Pool 0: allocPoint = 100, totalAllocPoint = 100 (100% of rewards)
Timeline:
t=0: Alice deposits 100 LP tokens
accRewardPerShare = 0
Alice rewardDebt = 0
t=100: Bob deposits 100 LP tokens
Pool update: reward = 100s * 10 = 1000 tokens
accRewardPerShare = 1000 * 1e18 / 100 = 10e18
Alice pending = (100 * 10e18 / 1e18) - 0 = 1000 tokens
Alice automatically claims 1000 tokens
Bob rewardDebt = 100 * 10e18 / 1e18 = 1000
t=200: Alice withdraws 100 LP tokens
Pool update: reward = 100s * 10 = 1000 tokens
New lpSupply = 200, so accRewardPerShare += 1000 * 1e18 / 200 = 5e18
accRewardPerShare = 15e18
Alice pending = (100 * 15e18 / 1e18) - 1000 = 500 tokens
Bob pending = (100 * 15e18 / 1e18) - 1000 = 500 tokens
Depositing LP Tokens
To start earning rewards, deposit your MonoSwap LP tokens into a farming pool:
function deposit(uint256 _pid, uint256 _amount) external;
| Parameter | Description |
|---|---|
_pid | Pool ID (index in the pool array) |
_amount | Number of LP tokens to deposit |
If you already have a position in the pool and there are pending rewards, they are automatically claimed during the deposit.
Steps
- Provide liquidity on MonoSwap to receive MONO-LP tokens (see Trading & Liquidity)
- Approve the LPFarming contract to spend your LP tokens
- Call
deposit()with the pool ID and amount
Withdrawing LP Tokens
To stop farming and reclaim your LP tokens:
function withdraw(uint256 _pid, uint256 _amount) external;
| Parameter | Description |
|---|---|
_pid | Pool ID |
_amount | Number of LP tokens to withdraw (must be ≤ your deposited amount) |
Pending rewards are automatically claimed during withdrawal. You can withdraw 0 tokens to claim rewards without removing your position.
Claiming Rewards
Rewards are claimed automatically on every deposit() and withdraw() call. To claim without changing your position, simply call:
// Claim rewards without depositing or withdrawing
deposit(pid, 0); // or withdraw(pid, 0);
Viewing Pending Rewards
Check how many reward tokens you have earned but not yet claimed:
function pendingReward(uint256 _pid, address _user)
external view returns (uint256);
Partial Rewards
If the vault runs out of reward tokens, the contract transfers whatever is available and emits a PartialReward event:
event PartialReward(address indexed user, uint256 promised, uint256 paid);
This ensures users are never blocked from withdrawing LP tokens even if the reward vault is empty. No rewards are lost -- they simply cannot be paid until the vault is refilled.
Emergency Withdrawal
In case of emergency, users can withdraw all staked LP tokens without claiming rewards:
function emergencyWithdraw(uint256 _pid) external;
This function:
- Transfers all of the user's staked LP tokens back to them
- Sets the user's
amountandrewardDebtto 0 - Forfeits any unclaimed rewards
Use this only if the normal withdraw() function is not working or if you need to exit immediately regardless of pending rewards.
Contract Parameters
| Parameter | Value | Description |
|---|---|---|
rewardToken | Set at deployment (immutable) | The ERC-20 token distributed as rewards |
rewardPerSecond | Owner-configurable | Tokens distributed per second across all pools |
MAX_REWARD_PER_SECOND | 100 tokens/sec | Circuit breaker preventing excessive emission |
ACC_PRECISION | 1e18 | Scaling factor for reward calculation precision |
Owner Functions
| Function | Description |
|---|---|
add(allocPoint, lpToken) | Add a new farming pool |
set(pid, allocPoint) | Update a pool's allocation points |
setRewardPerSecond(rate) | Change the global reward emission rate |
pause() / unpause() | Pause or resume deposits (owner or emergency admin) |
setEmergencyAdmin(address) | Set emergency admin for pause/unpause |
When setRewardPerSecond() is called, all pools are updated first to ensure rewards are accurately split at the previous rate before the new rate takes effect.
Checking Farm Health
Use rewardHealth() to check the farm's solvency:
function rewardHealth() external view returns (
uint256 available, // Reward tokens currently in the contract
uint256 emittingPerDay, // rewardPerSecond * 86400
uint256 runwaySeconds // available / rewardPerSecond
);
Example output:
available: 50,000 LYTH
emittingPerDay: 864 LYTH/day (10 tokens/sec)
runwaySeconds: 5,000 seconds (~57.9 days)
If runwaySeconds is low, the owner needs to deposit more reward tokens to maintain the emission rate.
Security
- ReentrancyGuard: All state-changing functions are protected against reentrancy
- Pausable: Deposits can be paused by the owner or emergency admin; withdrawals and emergency withdrawals remain available even when paused
- No minting: The contract cannot create new tokens -- it can only distribute what has been deposited
- Circuit breaker:
rewardPerSecondcannot exceedMAX_REWARD_PER_SECOND(100 tokens/sec) - Duplicate prevention: The same LP token cannot be added to multiple pools
- Safe transfer: Uses
_safeRewardTransfer()to handle vault depletion gracefully
How to Farm on MonoHub
Step 1: Provide Liquidity
Navigate to the Trading page and add liquidity to a MonoSwap pair. You will receive MONO-LP tokens.
Step 2: Navigate to Yield Farming
Go to the DeFi Center and select Yield Farming at /defi-center/yield-farming.
Step 3: Choose a Pool
Browse available farming pools. Each pool displays:
- The LP token pair (e.g., TOKEN/LYTH)
- Current APR (based on reward rate and total staked)
- Total value locked (TVL)
- Your staked amount and pending rewards
Step 4: Deposit
Select a pool, enter the amount of LP tokens to stake, approve if needed, and confirm the deposit.
Step 5: Claim Rewards
Return at any time to claim accumulated rewards or add more LP tokens.
Step 6: Withdraw
When you want to exit, withdraw your LP tokens (rewards are claimed automatically). Then remove liquidity on MonoSwap to get your original tokens back.
Related
- Trading & Liquidity -- How to get LP tokens
- MonoPump -- Token launcher (graduated tokens can have farming pools)
- DeFi Overview -- Full ecosystem overview