Ana içeriğe geç

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:

FieldTypeDescription
lpTokenaddressThe MonoSwap LP token to stake
allocPointuint256Pool's share of total rewards
lastRewardTimeuint256Last time rewards were calculated
accRewardPerShareuint256Accumulated 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;
ParameterDescription
_pidPool ID (index in the pool array)
_amountNumber 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

  1. Provide liquidity on MonoSwap to receive MONO-LP tokens (see Trading & Liquidity)
  2. Approve the LPFarming contract to spend your LP tokens
  3. 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;
ParameterDescription
_pidPool ID
_amountNumber 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 amount and rewardDebt to 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

ParameterValueDescription
rewardTokenSet at deployment (immutable)The ERC-20 token distributed as rewards
rewardPerSecondOwner-configurableTokens distributed per second across all pools
MAX_REWARD_PER_SECOND100 tokens/secCircuit breaker preventing excessive emission
ACC_PRECISION1e18Scaling factor for reward calculation precision

Owner Functions

FunctionDescription
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: rewardPerSecond cannot exceed MAX_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.