Skip to content

代币快照系统

代币快照是一个用于记录代币持有者在特定时间点状态的系统。本教程将介绍如何实现一个安全可靠的快照系统。

功能特性

  • 快照管理
  • 状态记录
  • 权限控制
  • 数据查询
  • 紧急处理

合约实现

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/security/Pausable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
 * @title TokenSnapshot
 * @dev 代币快照合约实现
 */
contract TokenSnapshot is Ownable, ReentrancyGuard, Pausable {
    using SafeMath for uint256;

    // 快照信息
    struct SnapshotInfo {
        uint256 id;             // 快照ID
        uint256 timestamp;      // 时间戳
        string description;     // 描述
        bool isProcessed;       // 是否处理
        address operator;       // 操作者
    }

    // 余额快照
    struct BalanceSnapshot {
        uint256 snapshotId;    // 快照ID
        uint256 balance;       // 余额
        uint256 timestamp;     // 时间戳
        bool isValid;          // 是否有效
    }

    // 配置信息
    struct SnapshotConfig {
        uint256 minInterval;   // 最小间隔
        uint256 maxSnapshots;  // 最大快照数
        bool requiresReason;   // 是否需要原因
        bool isActive;         // 是否激活
    }

    // 状态变量
    mapping(uint256 => SnapshotInfo) public snapshots;                    // 快照
    mapping(uint256 => mapping(address => BalanceSnapshot)) public balanceSnapshots;  // 余额快照
    mapping(address => bool) public operators;                            // 操作员
    SnapshotConfig public config;                                        // 配置信息
    uint256 public snapshotCount;                                        // 快照数量
    IERC20 public token;                                                // 代币合约

    // 事件
    event SnapshotCreated(uint256 indexed snapshotId, uint256 timestamp, string description);
    event SnapshotProcessed(uint256 indexed snapshotId, uint256 timestamp);
    event BalanceSnapshotted(uint256 indexed snapshotId, address indexed account, uint256 balance);
    event OperatorUpdated(address indexed operator, bool status);
    event ConfigUpdated(uint256 minInterval, uint256 maxSnapshots);

    /**
     * @dev 构造函数
     */
    constructor(
        address _token,
        uint256 _minInterval,
        uint256 _maxSnapshots
    ) {
        require(_token != address(0), "Invalid token");
        token = IERC20(_token);
        config = SnapshotConfig({
            minInterval: _minInterval,
            maxSnapshots: _maxSnapshots,
            requiresReason: true,
            isActive: true
        });
    }

    /**
     * @dev 创建快照
     */
    function createSnapshot(
        string calldata _description
    ) external onlyOperator whenNotPaused returns (uint256) {
        require(config.isActive, "Snapshot not active");
        require(snapshotCount < config.maxSnapshots, "Too many snapshots");
        
        if (snapshotCount > 0) {
            require(
                block.timestamp >= snapshots[snapshotCount].timestamp.add(config.minInterval),
                "Too frequent"
            );
        }

        if (config.requiresReason) {
            require(bytes(_description).length > 0, "Description required");
        }

        uint256 snapshotId = snapshotCount + 1;
        snapshots[snapshotId] = SnapshotInfo({
            id: snapshotId,
            timestamp: block.timestamp,
            description: _description,
            isProcessed: false,
            operator: msg.sender
        });

        snapshotCount = snapshotId;
        emit SnapshotCreated(snapshotId, block.timestamp, _description);
        return snapshotId;
    }

    /**
     * @dev 处理快照
     */
    function processSnapshot(
        uint256 _snapshotId,
        address[] calldata _accounts
    ) external onlyOperator whenNotPaused {
        SnapshotInfo storage snapshot = snapshots[_snapshotId];
        require(snapshot.id > 0, "Snapshot not found");
        require(!snapshot.isProcessed, "Already processed");

        for (uint256 i = 0; i < _accounts.length; i++) {
            address account = _accounts[i];
            require(account != address(0), "Invalid account");

            uint256 balance = token.balanceOf(account);
            balanceSnapshots[_snapshotId][account] = BalanceSnapshot({
                snapshotId: _snapshotId,
                balance: balance,
                timestamp: block.timestamp,
                isValid: true
            });

            emit BalanceSnapshotted(_snapshotId, account, balance);
        }

        snapshot.isProcessed = true;
        emit SnapshotProcessed(_snapshotId, block.timestamp);
    }

    /**
     * @dev 批量处理快照
     */
    function batchProcessSnapshot(
        uint256 _snapshotId,
        address[] calldata _accounts,
        uint256[] calldata _balances
    ) external onlyOperator whenNotPaused {
        require(_accounts.length == _balances.length, "Length mismatch");
        
        SnapshotInfo storage snapshot = snapshots[_snapshotId];
        require(snapshot.id > 0, "Snapshot not found");
        require(!snapshot.isProcessed, "Already processed");

        for (uint256 i = 0; i < _accounts.length; i++) {
            address account = _accounts[i];
            uint256 balance = _balances[i];
            require(account != address(0), "Invalid account");

            balanceSnapshots[_snapshotId][account] = BalanceSnapshot({
                snapshotId: _snapshotId,
                balance: balance,
                timestamp: block.timestamp,
                isValid: true
            });

            emit BalanceSnapshotted(_snapshotId, account, balance);
        }

        snapshot.isProcessed = true;
        emit SnapshotProcessed(_snapshotId, block.timestamp);
    }

    /**
     * @dev 获取快照信息
     */
    function getSnapshotInfo(
        uint256 _snapshotId
    ) external view returns (
        uint256 id,
        uint256 timestamp,
        string memory description,
        bool isProcessed,
        address operator
    ) {
        SnapshotInfo storage snapshot = snapshots[_snapshotId];
        return (
            snapshot.id,
            snapshot.timestamp,
            snapshot.description,
            snapshot.isProcessed,
            snapshot.operator
        );
    }

    /**
     * @dev 获取余额快照
     */
    function getBalanceSnapshot(
        uint256 _snapshotId,
        address _account
    ) external view returns (
        uint256 snapshotId,
        uint256 balance,
        uint256 timestamp,
        bool isValid
    ) {
        BalanceSnapshot storage snapshot = balanceSnapshots[_snapshotId][_account];
        return (
            snapshot.snapshotId,
            snapshot.balance,
            snapshot.timestamp,
            snapshot.isValid
        );
    }

    /**
     * @dev 更新操作员
     */
    function updateOperator(
        address _operator,
        bool _status
    ) external onlyOwner {
        operators[_operator] = _status;
        emit OperatorUpdated(_operator, _status);
    }

    /**
     * @dev 更新配置
     */
    function updateConfig(
        uint256 _minInterval,
        uint256 _maxSnapshots,
        bool _requiresReason
    ) external onlyOwner {
        config.minInterval = _minInterval;
        config.maxSnapshots = _maxSnapshots;
        config.requiresReason = _requiresReason;

        emit ConfigUpdated(_minInterval, _maxSnapshots);
    }

    /**
     * @dev 设置快照系统状态
     */
    function setActive(bool _isActive) external onlyOwner {
        config.isActive = _isActive;
    }

    /**
     * @dev 暂停/恢复合约
     */
    function setPaused(bool _paused) external onlyOwner {
        if (_paused) {
            _pause();
        } else {
            _unpause();
        }
    }

    /**
     * @dev 批量获取快照信息
     */
    function batchGetSnapshotInfo(
        uint256[] calldata _snapshotIds
    ) external view returns (
        uint256[] memory ids,
        uint256[] memory timestamps,
        string[] memory descriptions,
        bool[] memory processedFlags,
        address[] memory operators
    ) {
        ids = new uint256[](_snapshotIds.length);
        timestamps = new uint256[](_snapshotIds.length);
        descriptions = new string[](_snapshotIds.length);
        processedFlags = new bool[](_snapshotIds.length);
        operators = new address[](_snapshotIds.length);

        for (uint256 i = 0; i < _snapshotIds.length; i++) {
            SnapshotInfo storage snapshot = snapshots[_snapshotIds[i]];
            ids[i] = snapshot.id;
            timestamps[i] = snapshot.timestamp;
            descriptions[i] = snapshot.description;
            processedFlags[i] = snapshot.isProcessed;
            operators[i] = snapshot.operator;
        }

        return (ids, timestamps, descriptions, processedFlags, operators);
    }

    /**
     * @dev 批量获取余额快照
     */
    function batchGetBalanceSnapshot(
        uint256 _snapshotId,
        address[] calldata _accounts
    ) external view returns (
        uint256[] memory balances,
        uint256[] memory timestamps,
        bool[] memory validFlags
    ) {
        balances = new uint256[](_accounts.length);
        timestamps = new uint256[](_accounts.length);
        validFlags = new bool[](_accounts.length);

        for (uint256 i = 0; i < _accounts.length; i++) {
            BalanceSnapshot storage snapshot = balanceSnapshots[_snapshotId][_accounts[i]];
            balances[i] = snapshot.balance;
            timestamps[i] = snapshot.timestamp;
            validFlags[i] = snapshot.isValid;
        }

        return (balances, timestamps, validFlags);
    }

    /**
     * @dev 获取快照统计信息
     */
    function getSnapshotStats() external view returns (
        uint256 totalSnapshots,
        uint256 processedSnapshots,
        uint256 lastSnapshotTime,
        bool isActive,
        bool isPaused
    ) {
        uint256 processed = 0;
        for (uint256 i = 1; i <= snapshotCount; i++) {
            if (snapshots[i].isProcessed) {
                processed++;
            }
        }

        return (
            snapshotCount,
            processed,
            snapshotCount > 0 ? snapshots[snapshotCount].timestamp : 0,
            config.isActive,
            paused()
        );
    }

    /**
     * @dev 验证快照
     */
    function validateSnapshot(
        uint256 _snapshotId,
        address _account,
        uint256 _expectedBalance
    ) external view returns (bool) {
        BalanceSnapshot storage snapshot = balanceSnapshots[_snapshotId][_account];
        return snapshot.isValid && snapshot.balance == _expectedBalance;
    }

    /**
     * @dev 清理过期快照
     */
    function cleanupSnapshots(uint256[] calldata _snapshotIds) external onlyOwner {
        for (uint256 i = 0; i < _snapshotIds.length; i++) {
            uint256 snapshotId = _snapshotIds[i];
            require(snapshots[snapshotId].id > 0, "Snapshot not found");
            require(snapshots[snapshotId].isProcessed, "Snapshot not processed");

            delete snapshots[snapshotId];
            emit SnapshotProcessed(snapshotId, block.timestamp);
        }
    }

    /**
     * @dev 紧急提取
     */
    function emergencyWithdraw(
        address _token,
        address _to,
        uint256 _amount
    ) external onlyOwner {
        require(_token != address(token), "Cannot withdraw snapshot token");
        require(_to != address(0), "Invalid recipient");
        IERC20(_token).transfer(_to, _amount);
    }

    /**
     * @dev 检查是否是操作员
     */
    modifier onlyOperator() {
        require(operators[msg.sender] || msg.sender == owner(), "Not operator");
        _;
    }
}

关键概念

快照管理

快照功能包括:

  • 快照创建
  • 快照处理
  • 快照验证
  • 状态记录

权限管理

权限功能包括:

  • 操作员管理
  • 权限验证
  • 配置控制
  • 状态管理

数据管理

数据功能包括:

  • 余额记录
  • 数据验证
  • 批量处理
  • 查询统计

安全考虑

  1. 快照安全

    • 时间验证
    • 状态检查
    • 数据验证
    • 异常处理
  2. 权限安全

    • 操作验证
    • 权限检查
    • 配置控制
    • 状态保护
  3. 系统安全

    • 权限控制
    • 暂停机制
    • 重入防护
    • 状态同步
  4. 升级安全

    • 配置更新
    • 操作员调整
    • 状态迁移
    • 紧急处理

最佳实践

  1. 快照管理

    • 时间控制
    • 状态验证
    • 数据记录
    • 异常处理
  2. 权限管理

    • 操作员分配
    • 权限验证
    • 配置控制
    • 状态管理
  3. 数据管理

    • 数据验证
    • 批量处理
    • 查询优化
    • 存储优化
  4. 系统维护

    • 参数优化
    • 性能监控
    • 安全审计
    • 升级预案

扩展功能

  1. 多币种快照
  2. 增量快照
  3. 数据压缩
  4. 历史查询
  5. 数据分析

应用场景

  1. 治理投票

    • 权重计算
    • 投票验证
    • 结果统计
    • 权益分配
  2. 空投分发

    • 资格验证
    • 数量计算
    • 批量分发
    • 记录追踪
  3. 数据分析

    • 持仓分析
    • 流动性分析
    • 交易分析
    • 风险评估

总结

代币快照系统是DeFi生态的重要工具。通过本教程,你可以:

  • 实现快照功能
  • 管理数据记录
  • 优化查询效率
  • 提供数据分析

常见问题解答(FAQ)

基础概念

Q: 什么是代币快照?

A: 代币快照是在特定时间点记录所有代币持有者余额状态的机制,主要用途包括:

  • 空投分发
  • 治理投票
  • 奖励分配
  • 审计追踪
  • 状态回溯

Q: 快照的触发条件是什么?

A: 快照可以通过以下条件触发:

  • 时间间隔
  • 特定事件
  • 管理员操作
  • 治理提案
  • 系统状态变更

操作相关

Q: 如何创建快照?

A: 创建快照的步骤包括:

  • 确认操作权限
  • 检查时间间隔
  • 提供必要描述
  • 调用创建函数
  • 等待处理完成

Q: 如何查询快照数据?

A: 查询方式包括:

  • 按快照ID查询
  • 按账户地址查询
  • 按时间范围查询
  • 批量查询接口
  • 导出数据功能

安全相关

Q: 快照系统有哪些风险?

A: 主要风险包括:

  • 权限滥用
  • 数据不完整
  • 重放攻击
  • 存储溢出
  • 系统故障

Q: 如何确保系统安全?

A: 安全措施包括:

  • 多重授权
  • 数据验证
  • 状态检查
  • 完整日志
  • 应急机制

Released under the MIT License by Vogeb.