Liquidity Mining Contract Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Liquidity Mining Contract Development
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Liquidity Mining Contract Development

Liquidity mining is a mechanism where users provide liquidity and receive rewards in protocol tokens. SushiSwap in 2020 executed a "vampire attack" on Uniswap through exactly this: deployed an identical AMM with aggressive SUSHI rewards and within 24 hours attracted $1B in TVL. The mechanics work.

But behind the apparent simplicity of "stake LP token → get reward" lies non-trivial mathematics and several classes of vulnerabilities that regularly appear in production.

Reward Distribution Mathematics

Masterchef Algorithm (Synthetix StakingRewards)

The basic algorithm used by most protocols is based on accumulated reward value per stake unit (rewardPerTokenStored).

// Accumulated reward per token
uint256 public rewardPerTokenStored;

function rewardPerToken() public view returns (uint256) {
    if (totalSupply == 0) return rewardPerTokenStored;
    return rewardPerTokenStored + (
        (block.timestamp - lastUpdateTime) 
        * rewardRate 
        * 1e18 
        / totalSupply
    );
}

// Reward for specific user
function earned(address account) public view returns (uint256) {
    return (balanceOf[account] * 
        (rewardPerToken() - userRewardPerTokenPaid[account]) 
        / 1e18) 
        + rewards[account];
}

Key property: updating rewardPerTokenStored happens on every stake/withdraw/getReward, not on every block. This is an O(1) operation independent of the number of participants.

On each user interaction we store userRewardPerTokenPaid[user] = rewardPerToken(). The difference between the current rewardPerToken() and the stored value represents accumulated rewards since the last interaction.

This mathematics is correct under continuous time. In practice, discrete blocks create rounding errors that must be accounted for.

Boosted Rewards (ve-tokens)

Convex/Curve model: rewards depend not only on stake amount but also on locked governance tokens (veToken). Formula:

effective_balance = min(
    0.4 * balance + 0.6 * (totalSupply * veBal / veTotalSupply),
    balance
)

This creates an incentive to hold governance tokens, reduces sell pressure on reward tokens. Mathematically more complex, requires careful testing of edge cases with zero veBalance.

Multi-reward Distribution

When distributing multiple tokens simultaneously (e.g., protocol token + USDC fee sharing), architecture becomes more complex. Each reward token requires its own rewardPerTokenStored. OpenZeppelin doesn't provide a ready multi-reward implementation; we use Synthetix StakingMultiRewards as reference.

Vulnerabilities We've Seen in Audits

Inflation Attack on First Deposit

With empty staking contract (totalSupply = 0), the first user can manipulate rewardPerToken(). Attack: deploy contract, attacker makes micro-deposit (1 wei), contract accumulates all rewards for 1 wei, attacker gets disproportionately large share.

Solution: virtual initial balance (VIRTUAL_TOTAL_SUPPLY = 1e18) that is never withdrawn, or minimum deposit check.

Flash Loan Attack on Rewards

Attacker borrows flash loan on LP tokens, stakes huge amount, gets reward for one block, withdraws. With high rewardRate this can be profitable.

Protection: minimum staking period (lockup period, 1-7 days). Alternative: vesting rewards — don't release immediately, distribute linearly over N days. Even 24-hour vesting makes flash loan attacks unprofitable.

Griefing via updateReward

Functions stake, withdraw, getReward should call _updateReward(msg.sender). If this is a modifier — every call updates state for msg.sender. But if _updateReward is expensive (e.g., iterates over array), external user can call functions in loop and make contract inaccessible via gas griefing.

Solution: _updateReward must be O(1). Don't iterate over participant arrays.

Precision Loss with Small Amounts

When dividing rewardRate * dt / totalSupply, if totalSupply is very large and dt small, result can be 0 due to integer division. Rewards "evaporate".

Solution: scaling via 1e18 (or 1e27 for Aave-like precision), accumulating remainders.

Economics and Parameters

rewardRate — number of reward tokens per second. Calculated as rewardAmount / duration. On notifyRewardAmount() recalculated accounting for remaining period balance.

function notifyRewardAmount(uint256 reward) external onlyOwner {
    if (block.timestamp >= periodFinish) {
        rewardRate = reward / rewardsDuration;
    } else {
        uint256 remaining = periodFinish - block.timestamp;
        uint256 leftover = remaining * rewardRate;
        rewardRate = (reward + leftover) / rewardsDuration;
    }
    // ...
}

Emergency withdrawal — user should be able to withdraw stake even if contract is paused. Rewards don't need to be issued, but principal stake is mandatory. This is required not only by common sense but also by some jurisdictions.

Process

Design (0.5-1 day). Determine: one reward token or multiple, need boosting, minimum lockup, reward replenishment mechanism (manual vs automated).

Development (2-3 days). Main contract, events for indexing (The Graph subgraph built on events), tests with fuzz testing of arithmetic.

Testing (1-2 days). Cover: normal staking/withdrawal, flash loan scenario with minimum lockup, precision tests with boundary values, multi-user scenarios with different ratios.

Deployment. Separate deployer script with initial rewardRate configuration, transferOwnership to multisig.

For most projects, full cycle is 3-5 working days. Complex schemes with veToken boosting and multiple reward tokens — 8-12 days.