Skip to content

代币分红系统

1. 系统概述

代币分红系统是一个基于 Solidity 实现的多代币分红平台,支持用户通过质押单一代币来获得多种代币的分红收益。系统实现了灵活的多代币分红机制和动态的收益分配功能。

1.1 主要特点

  • 多代币分红:支持多种代币作为分红奖励
  • 单一质押:使用单一代币进行质押
  • 动态分配:实时计算和更新收益
  • 批量领取:支持一键领取所有代币奖励
  • 灵活管理:支持添加新的分红代币
  • 精确计算:高精度的收益计算机制

2. 合约实现

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

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

/**
 * @title TokenDividend
 * @dev 代币分红合约
 */
contract TokenDividend is Ownable, ReentrancyGuard {
    using SafeMath for uint256;

    // 分红代币信息
    struct RewardToken {
        IERC20 token;             // 代币合约
        uint256 totalRewards;     // 总奖励
        uint256 rewardsPerShare;  // 每股奖励
        uint256 lastUpdateTime;   // 最后更新时间
        bool isActive;            // 是否激活
    }

    // 用户分红信息
    struct UserInfo {
        uint256 amount;           // 质押数量
        mapping(uint256 => uint256) rewardDebt;  // 每个代币的奖励债务
        uint256 lastClaimTime;    // 上次领取时间
    }

    // 状态变量
    IERC20 public stakeToken;                    // 质押代币
    uint256 public totalStaked;                  // 总质押量
    uint256 public rewardTokenCount;             // 奖励代币数量
    uint256 public minStakeAmount;               // 最小质押数量
    uint256 public minClaimInterval;             // 最小领取间隔
    bool public paused;                          // 暂停状态

    mapping(uint256 => RewardToken) public rewardTokens;      // 奖励代币列表
    mapping(address => UserInfo) public userInfo;             // 用户信息

    // 事件
    event RewardTokenAdded(uint256 indexed tokenId, address token);
    event RewardTokenUpdated(uint256 indexed tokenId, bool isActive);
    event Staked(address indexed user, uint256 amount);
    event Unstaked(address indexed user, uint256 amount);
    event RewardDistributed(uint256 indexed tokenId, uint256 amount);
    event RewardClaimed(address indexed user, uint256 indexed tokenId, uint256 amount);

    /**
     * @dev 构造函数
     */
    constructor(
        IERC20 _stakeToken,
        uint256 _minStakeAmount,
        uint256 _minClaimInterval
    ) {
        require(address(_stakeToken) != address(0), "Invalid stake token");
        stakeToken = _stakeToken;
        minStakeAmount = _minStakeAmount;
        minClaimInterval = _minClaimInterval;
    }

    /**
     * @dev 添加奖励代币
     */
    function addRewardToken(IERC20 _token) external onlyOwner {
        require(address(_token) != address(0), "Invalid token");
        
        rewardTokens[rewardTokenCount] = RewardToken({
            token: _token,
            totalRewards: 0,
            rewardsPerShare: 0,
            lastUpdateTime: block.timestamp,
            isActive: true
        });

        emit RewardTokenAdded(rewardTokenCount, address(_token));
        rewardTokenCount = rewardTokenCount.add(1);
    }

    /**
     * @dev 质押代币
     */
    function stake(uint256 _amount) external nonReentrant {
        require(!paused, "System paused");
        require(_amount >= minStakeAmount, "Amount too small");

        UserInfo storage user = userInfo[msg.sender];

        // 如果已有质押,先领取现有奖励
        if (user.amount > 0) {
            claimAllRewards();
        }

        // 转入质押代币
        stakeToken.transferFrom(msg.sender, address(this), _amount);
        
        // 更新用户信息
        user.amount = user.amount.add(_amount);
        totalStaked = totalStaked.add(_amount);

        // 更新奖励债务
        for (uint256 i = 0; i < rewardTokenCount; i++) {
            if (rewardTokens[i].isActive) {
                user.rewardDebt[i] = user.amount.mul(rewardTokens[i].rewardsPerShare).div(1e12);
            }
        }

        emit Staked(msg.sender, _amount);
    }

    /**
     * @dev 解除质押
     */
    function unstake(uint256 _amount) external nonReentrant {
        UserInfo storage user = userInfo[msg.sender];
        require(user.amount >= _amount, "Insufficient stake");

        // 先领取所有奖励
        claimAllRewards();

        // 更新用户信息
        user.amount = user.amount.sub(_amount);
        totalStaked = totalStaked.sub(_amount);

        // 更新奖励债务
        for (uint256 i = 0; i < rewardTokenCount; i++) {
            if (rewardTokens[i].isActive) {
                user.rewardDebt[i] = user.amount.mul(rewardTokens[i].rewardsPerShare).div(1e12);
            }
        }

        // 转出质押代币
        stakeToken.transfer(msg.sender, _amount);

        emit Unstaked(msg.sender, _amount);
    }

    /**
     * @dev 分发奖励
     */
    function distributeReward(uint256 _tokenId, uint256 _amount) external nonReentrant {
        require(_tokenId < rewardTokenCount, "Invalid token ID");
        require(_amount > 0, "Amount must be greater than 0");

        RewardToken storage rToken = rewardTokens[_tokenId];
        require(rToken.isActive, "Token not active");

        // 转入奖励代币
        rToken.token.transferFrom(msg.sender, address(this), _amount);

        // 更新奖励信息
        rToken.totalRewards = rToken.totalRewards.add(_amount);
        if (totalStaked > 0) {
            rToken.rewardsPerShare = rToken.rewardsPerShare.add(_amount.mul(1e12).div(totalStaked));
        }
        rToken.lastUpdateTime = block.timestamp;

        emit RewardDistributed(_tokenId, _amount);
    }

    /**
     * @dev 领取所有奖励
     */
    function claimAllRewards() public nonReentrant {
        require(!paused, "System paused");
        UserInfo storage user = userInfo[msg.sender];
        require(
            block.timestamp >= user.lastClaimTime.add(minClaimInterval),
            "Too frequent"
        );

        for (uint256 i = 0; i < rewardTokenCount; i++) {
            if (rewardTokens[i].isActive) {
                uint256 pending = pendingReward(i, msg.sender);
                if (pending > 0) {
                    user.rewardDebt[i] = user.amount.mul(rewardTokens[i].rewardsPerShare).div(1e12);
                    rewardTokens[i].token.transfer(msg.sender, pending);
                    emit RewardClaimed(msg.sender, i, pending);
                }
            }
        }

        user.lastClaimTime = block.timestamp;
    }

    /**
     * @dev 计算待领取奖励
     */
    function pendingReward(uint256 _tokenId, address _user) public view returns (uint256) {
        require(_tokenId < rewardTokenCount, "Invalid token ID");
        
        UserInfo storage user = userInfo[_user];
        RewardToken storage rToken = rewardTokens[_tokenId];

        if (user.amount == 0 || !rToken.isActive) {
            return 0;
        }

        uint256 accReward = user.amount.mul(rToken.rewardsPerShare).div(1e12);
        return accReward.sub(user.rewardDebt[_tokenId]);
    }

    /**
     * @dev 批量查询待领取奖励
     */
    function pendingRewards(address _user) external view returns (uint256[] memory) {
        uint256[] memory rewards = new uint256[](rewardTokenCount);
        for (uint256 i = 0; i < rewardTokenCount; i++) {
            rewards[i] = pendingReward(i, _user);
        }
        return rewards;
    }

    /**
     * @dev 设置奖励代币状态
     */
    function setRewardTokenStatus(uint256 _tokenId, bool _isActive) external onlyOwner {
        require(_tokenId < rewardTokenCount, "Invalid token ID");
        rewardTokens[_tokenId].isActive = _isActive;
        emit RewardTokenUpdated(_tokenId, _isActive);
    }

    /**
     * @dev 设置最小质押数量
     */
    function setMinStakeAmount(uint256 _amount) external onlyOwner {
        minStakeAmount = _amount;
    }

    /**
     * @dev 设置最小领取间隔
     */
    function setMinClaimInterval(uint256 _interval) external onlyOwner {
        minClaimInterval = _interval;
    }

    /**
     * @dev 暂停/恢复系统
     */
    function setPaused(bool _paused) external onlyOwner {
        paused = _paused;
    }

    /**
     * @dev 获取用户信息
     */
    function getUserInfo(address _user) external view returns (
        uint256 amount,
        uint256 lastClaimTime,
        uint256[] memory pendingRewards
    ) {
        UserInfo storage user = userInfo[_user];
        uint256[] memory rewards = new uint256[](rewardTokenCount);
        
        for (uint256 i = 0; i < rewardTokenCount; i++) {
            rewards[i] = pendingReward(i, _user);
        }

        return (user.amount, user.lastClaimTime, rewards);
    }

    /**
     * @dev 获取奖励代币信息
     */
    function getRewardTokenInfo(uint256 _tokenId) external view returns (
        address token,
        uint256 totalRewards,
        uint256 rewardsPerShare,
        uint256 lastUpdateTime,
        bool isActive
    ) {
        require(_tokenId < rewardTokenCount, "Invalid token ID");
        RewardToken storage rToken = rewardTokens[_tokenId];
        
        return (
            address(rToken.token),
            rToken.totalRewards,
            rToken.rewardsPerShare,
            rToken.lastUpdateTime,
            rToken.isActive
        );
    }
}

3. 功能说明

3.1 质押管理

  • 质押代币
  • 解除质押
  • 质押状态查询

3.2 分红管理

  • 添加分红代币
  • 分发分红
  • 领取分红

3.3 状态查询

  • 用户信息查询
  • 分红代币信息查询
  • 待领取分红查询

4. 安全机制

4.1 分红控制

  • 最小质押限制
  • 领取间隔控制
  • 暂停机制

4.2 访问控制

  • 权限管理
  • 重入保护
  • 参数验证

4.3 状态管理

  • 代币状态
  • 用户信息
  • 分红记录

5. 使用示例

5.1 添加分红代币

javascript
await tokenDividend.addRewardToken(rewardToken.address);

5.2 质押代币

javascript
const amount = ethers.utils.parseEther("100");
await tokenDividend.stake(amount);

5.3 领取分红

javascript
await tokenDividend.claimAllRewards();

6. 总结

该代币分红系统实现了完整的多代币分红功能,包括:

  • 多代币分红支持
  • 灵活的质押管理
  • 实时的收益计算
  • 批量领取功能
  • 完善的安全机制

系统通过精心设计的多代币分红机制和状态管理,为用户提供了灵活且高效的分红服务,支持多种代币的同时分红,极大地提升了用户的收益管理效率。

常见问题解答(FAQ)

1. 基本概念

Q: 什么是代币分红? A: 代币分红是一种代币经济模型,允许代币持有者按照其持有比例获得项目收益的分配。这种机制可以激励长期持有,并让用户分享项目成功带来的收益。

Q: 分红和质押奖励有什么区别? A: 主要区别在于:

  • 分红来源于项目实际收益
  • 质押奖励通常是通过通胀产生
  • 分红金额不固定,取决于项目表现
  • 分红可以是多种代币
  • 分红权重通常基于持有量

2. 功能相关

Q: 如何计算用户应得的分红? A: 计算公式如下:

solidity
userDividend = totalDividend * userBalance / totalSupply

Q: 支持哪些类型的分红? A: 支持以下分红类型:

  • ETH/原生代币分红
  • ERC20代币分红
  • 多代币组合分红
  • 定期分红
  • 即时分红

3. 安全相关

Q: 如何防止分红攻击? A: 采取以下措施:

  • 快照机制记录持仓
  • 锁定期设置
  • 最小持仓要求
  • 防重入保护
  • 金额验证

Q: 如何处理未领取的分红? A: 通过以下机制:

  • 设置领取期限
  • 超时后归入公池
  • 自动累积到下次
  • 记录历史分红
  • 提供批量领取

4. 优化相关

Q: 如何优化Gas消耗? A: 优化策略包括:

  • 批量处理分红
  • 使用累积点数机制
  • 优化存储结构
  • 合并同类操作
  • 使用事件代替存储

Q: 如何提高分红效率? A: 可以通过:

  • 自动分配机制
  • 智能合约钱包集成
  • 优化计算方法
  • 缓存中间结果
  • 并行处理分配

5. 实现细节

Q: 如何处理精度问题? A: 精度处理方案:

solidity
// 使用高精度计算
uint256 constant PRECISION = 1e18;
uint256 share = amount * PRECISION / total;
uint256 reward = share * balance / PRECISION;

Q: 如何实现多代币分红? A: 实现方案:

  • 维护支持的代币列表
  • 独立记录每种代币分红
  • 统一的分配机制
  • 灵活的领取接口
  • 完整的记录追踪

6. 最佳实践

Q: 设置分红周期需要考虑什么? A: 建议考虑:

  • 项目收益周期
  • Gas成本平衡
  • 用户体验需求
  • 市场波动影响
  • 运营策略调整

Q: 如何提高用户参与度? A: 可以通过:

  • 透明的分红机制
  • 实时的数据展示
  • 友好的用户界面
  • 定期的数据报告
  • 社区投票参与

7. 错误处理

Q: 常见错误及解决方案? A: 主要错误类型:

  • "No dividend available": 检查分红周期
  • "Already claimed": 验证领取状态
  • "Invalid amount": 确认数额计算
  • "Not eligible": 检查资格要求
  • "System paused": 等待系统恢复

Q: 如何处理分红失败? A: 处理机制:

  • 自动重试机制
  • 错误日志记录
  • 手动干预接口
  • 补偿机制
  • 用户通知系统

8. 升级维护

Q: 如何升级分红机制? A: 升级策略:

  • 使用代理合约
  • 渐进式更新
  • 数据迁移方案
  • 向后兼容
  • 充分的测试

Q: 如何监控分红系统? A: 监控方案:

  • 事件日志分析
  • 性能指标追踪
  • 用户行为分析
  • 异常监测
  • 定期审计

9. 与其他模块集成

Q: 如何与质押系统集成? A: 集成方案:

  • 统一的权重计算
  • 共享的用户数据
  • 协调的时间锁定
  • 综合的收益计算
  • 一致的接口设计

Q: 如何实现跨链分红? A: 实现方案:

  • 桥接协议集成
  • 跨链消息传递
  • 统一的分红标准
  • 安全的验证机制
  • 完整的状态同步

Released under the MIT License by Vogeb.