代币流动性挖矿
流动性挖矿是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代币存取
- 奖励实时计算
- 复投操作
- 紧急提现
安全考虑
资金安全
- 余额检查
- 转账验证
- 溢出保护
权限控制
- 管理员功能限制
- 暂停机制
- 参数验证
重入防护
- 使用ReentrancyGuard
- 状态更新顺序
- 外部调用保护
异常处理
- 紧急提现
- 状态恢复
- 错误处理
最佳实践
矿池配置
- 合理的奖励速率
- 适当的权重分配
- 定期参数调整
收益管理
- 奖励代币储备
- 通胀控制
- 可持续性规划
用户体验
- 简单的操作流程
- 清晰的收益展示
- 及时的状态更新
系统维护
- 定期检查状态
- 监控异常情况
- 及时处理问题
扩展功能
- 奖励乘数
- 锁定期设置
- 自动复投
- 推荐奖励
- 治理权益
总结
流动性挖矿系统是DeFi项目的重要组成部分。通过本教程,你可以:
- 实现完整的挖矿机制
- 建立安全的奖励系统
- 优化用户参与体验
- 确保系统可持续运行
常见问题解答(FAQ)
基础概念
Q: 什么是流动性挖矿?
A: 流动性挖矿是一种DeFi激励机制,主要特点包括:
- 提供流动性获得奖励
- 多池子并行挖矿
- 动态奖励分配
- 自动复投机制
- 灵活的提取机制
Q: 流动性挖矿有哪些类型?
A: 主要类型包括:
- 单币种挖矿
- 交易对挖矿
- 指数基金挖矿
- 稳定币挖矿
- 合成资产挖矿
操作相关
Q: 如何参与流动性挖矿?
A: 参与步骤包括:
- 准备LP代币
- 选择挖矿池子
- 质押LP代币
- 等待收益生成
- 定期领取奖励
Q: 如何管理挖矿收益?
A: 管理方法包括:
- 监控收益率
- 计算投资回报
- 选择复投时机
- 调整质押策略
- 优化池子选择
安全相关
Q: 流动性挖矿有哪些风险?
A: 主要风险包括:
- 无常损失风险
- 智能合约风险
- 代币价格波动
- 收益率变化
- 资金池挤兑
Q: 如何降低挖矿风险?
A: 防范措施包括:
- 分散投资池子
- 定期检查收益
- 设置止损策略
- 关注项目进展
- 及时调整仓位