Skip to content

代币流动性挖矿

流动性挖矿是DeFi生态中重要的激励机制,用于吸引用户提供流动性。本教程将介绍如何实现一个完整的流动性挖矿系统。

功能特性

  • LP代币质押挖矿
  • 多池子支持
  • 动态奖励分配
  • 复投机制
  • 紧急提现功能

合约实现

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

contract LiquidityMining is Ownable, ReentrancyGuard, Pausable {
    // 矿池信息
    struct Pool {
        IERC20 lpToken;        // LP代币地址
        IERC20 rewardToken;    // 奖励代币地址
        uint256 lastRewardBlock;// 上次更新奖励的区块
        uint256 accRewardPerShare; // 每份额累积奖励
        uint256 allocPoint;     // 分配权重
        uint256 totalStaked;    // 总质押量
    }

    // 用户在池子中的信息
    struct UserInfo {
        uint256 amount;         // 质押数量
        uint256 rewardDebt;     // 奖励债务
        uint256 pendingRewards; // 待领取奖励
    }

    // 矿池列表
    Pool[] public pools;
    // 用户信息映射 poolId => user => info
    mapping(uint256 => mapping(address => UserInfo)) public userInfo;
    
    // 每区块奖励
    uint256 public rewardPerBlock;
    // 总分配权重
    uint256 public totalAllocPoint;
    // 开始挖矿的区块
    uint256 public startBlock;
    // 结束挖矿的区块
    uint256 public endBlock;

    // 事件
    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event RewardPaid(address indexed user, uint256 indexed pid, uint256 amount);
    event PoolAdded(uint256 indexed pid, address lpToken, address rewardToken, uint256 allocPoint);
    event PoolUpdated(uint256 indexed pid, uint256 allocPoint);

    constructor(
        uint256 _rewardPerBlock,
        uint256 _startBlock,
        uint256 _endBlock
    ) {
        rewardPerBlock = _rewardPerBlock;
        startBlock = _startBlock;
        endBlock = _endBlock;
    }

    // 添加新的矿池
    function addPool(
        IERC20 _lpToken,
        IERC20 _rewardToken,
        uint256 _allocPoint,
        bool _withUpdate
    ) external onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
        totalAllocPoint += _allocPoint;
        pools.push(Pool({
            lpToken: _lpToken,
            rewardToken: _rewardToken,
            lastRewardBlock: lastRewardBlock,
            accRewardPerShare: 0,
            allocPoint: _allocPoint,
            totalStaked: 0
        }));
        emit PoolAdded(pools.length - 1, address(_lpToken), address(_rewardToken), _allocPoint);
    }

    // 更新矿池分配权重
    function setPoolAllocPoint(
        uint256 _pid,
        uint256 _allocPoint,
        bool _withUpdate
    ) external onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        totalAllocPoint = totalAllocPoint - pools[_pid].allocPoint + _allocPoint;
        pools[_pid].allocPoint = _allocPoint;
        emit PoolUpdated(_pid, _allocPoint);
    }

    // 更新所有矿池
    function massUpdatePools() public {
        uint256 length = pools.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            updatePool(pid);
        }
    }

    // 更新单个矿池
    function updatePool(uint256 _pid) public {
        Pool storage pool = pools[_pid];
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        if (pool.totalStaked == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
        uint256 reward = multiplier * rewardPerBlock * pool.allocPoint / totalAllocPoint;
        pool.accRewardPerShare += reward * 1e12 / pool.totalStaked;
        pool.lastRewardBlock = block.number;
    }

    // 质押LP代币
    function deposit(uint256 _pid, uint256 _amount) external nonReentrant whenNotPaused {
        Pool storage pool = pools[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount * pool.accRewardPerShare / 1e12 - user.rewardDebt;
            if (pending > 0) {
                user.pendingRewards += pending;
            }
        }
        if (_amount > 0) {
            pool.lpToken.transferFrom(msg.sender, address(this), _amount);
            user.amount += _amount;
            pool.totalStaked += _amount;
        }
        user.rewardDebt = user.amount * pool.accRewardPerShare / 1e12;
        emit Deposit(msg.sender, _pid, _amount);
    }

    // 提取LP代币
    function withdraw(uint256 _pid, uint256 _amount) external nonReentrant {
        Pool storage pool = pools[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not enough");
        updatePool(_pid);
        uint256 pending = user.amount * pool.accRewardPerShare / 1e12 - user.rewardDebt;
        if (pending > 0) {
            user.pendingRewards += pending;
        }
        if (_amount > 0) {
            user.amount -= _amount;
            pool.totalStaked -= _amount;
            pool.lpToken.transfer(msg.sender, _amount);
        }
        user.rewardDebt = user.amount * pool.accRewardPerShare / 1e12;
        emit Withdraw(msg.sender, _pid, _amount);
    }

    // 领取奖励
    function harvest(uint256 _pid) external nonReentrant {
        Pool storage pool = pools[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        uint256 pending = user.amount * pool.accRewardPerShare / 1e12 - user.rewardDebt;
        if (pending > 0 || user.pendingRewards > 0) {
            uint256 totalReward = pending + user.pendingRewards;
            user.pendingRewards = 0;
            pool.rewardToken.transfer(msg.sender, totalReward);
            emit RewardPaid(msg.sender, _pid, totalReward);
        }
        user.rewardDebt = user.amount * pool.accRewardPerShare / 1e12;
    }

    // 紧急提现
    function emergencyWithdraw(uint256 _pid) external nonReentrant {
        Pool storage pool = pools[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        uint256 amount = user.amount;
        user.amount = 0;
        user.rewardDebt = 0;
        user.pendingRewards = 0;
        pool.totalStaked -= amount;
        pool.lpToken.transfer(msg.sender, amount);
        emit EmergencyWithdraw(msg.sender, _pid, amount);
    }

    // 计算区块奖励倍数
    function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
        if (_to <= endBlock) {
            return _to - _from;
        } else if (_from >= endBlock) {
            return 0;
        } else {
            return endBlock - _from;
        }
    }

    // 查看待领取奖励
    function pendingReward(uint256 _pid, address _user) external view returns (uint256) {
        Pool storage pool = pools[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accRewardPerShare = pool.accRewardPerShare;
        if (block.number > pool.lastRewardBlock && pool.totalStaked != 0) {
            uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
            uint256 reward = multiplier * rewardPerBlock * pool.allocPoint / totalAllocPoint;
            accRewardPerShare += reward * 1e12 / pool.totalStaked;
        }
        return user.amount * accRewardPerShare / 1e12 - user.rewardDebt + user.pendingRewards;
    }

    // 暂停/恢复(管理员功能)
    function setPaused(bool _paused) external onlyOwner {
        if (_paused) {
            _pause();
        } else {
            _unpause();
        }
    }
}

关键概念

矿池管理

矿池系统支持:

  • 多池子并行运行
  • 动态权重分配
  • 独立的奖励代币
  • 灵活的参数调整

奖励计算

奖励机制包括:

  • 区块奖励分配
  • 权重比例计算
  • 累积奖励更新
  • 用户份额计算

质押操作

质押功能支持:

  • LP代币存取
  • 奖励实时计算
  • 复投操作
  • 紧急提现

安全考虑

  1. 资金安全

    • 余额检查
    • 转账验证
    • 溢出保护
  2. 权限控制

    • 管理员功能限制
    • 暂停机制
    • 参数验证
  3. 重入防护

    • 使用ReentrancyGuard
    • 状态更新顺序
    • 外部调用保护
  4. 异常处理

    • 紧急提现
    • 状态恢复
    • 错误处理

最佳实践

  1. 矿池配置

    • 合理的奖励速率
    • 适当的权重分配
    • 定期参数调整
  2. 收益管理

    • 奖励代币储备
    • 通胀控制
    • 可持续性规划
  3. 用户体验

    • 简单的操作流程
    • 清晰的收益展示
    • 及时的状态更新
  4. 系统维护

    • 定期检查状态
    • 监控异常情况
    • 及时处理问题

扩展功能

  1. 奖励乘数
  2. 锁定期设置
  3. 自动复投
  4. 推荐奖励
  5. 治理权益

总结

流动性挖矿系统是DeFi项目的重要组成部分。通过本教程,你可以:

  • 实现完整的挖矿机制
  • 建立安全的奖励系统
  • 优化用户参与体验
  • 确保系统可持续运行

常见问题解答(FAQ)

基础概念

Q: 什么是流动性挖矿?

A: 流动性挖矿是一种DeFi激励机制,主要特点包括:

  • 提供流动性获得奖励
  • 多池子并行挖矿
  • 动态奖励分配
  • 自动复投机制
  • 灵活的提取机制

Q: 流动性挖矿有哪些类型?

A: 主要类型包括:

  • 单币种挖矿
  • 交易对挖矿
  • 指数基金挖矿
  • 稳定币挖矿
  • 合成资产挖矿

操作相关

Q: 如何参与流动性挖矿?

A: 参与步骤包括:

  • 准备LP代币
  • 选择挖矿池子
  • 质押LP代币
  • 等待收益生成
  • 定期领取奖励

Q: 如何管理挖矿收益?

A: 管理方法包括:

  • 监控收益率
  • 计算投资回报
  • 选择复投时机
  • 调整质押策略
  • 优化池子选择

安全相关

Q: 流动性挖矿有哪些风险?

A: 主要风险包括:

  • 无常损失风险
  • 智能合约风险
  • 代币价格波动
  • 收益率变化
  • 资金池挤兑

Q: 如何降低挖矿风险?

A: 防范措施包括:

  • 分散投资池子
  • 定期检查收益
  • 设置止损策略
  • 关注项目进展
  • 及时调整仓位

Released under the MIT License by Vogeb.