Solidity开发ERC20智能合约claim token的功能

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract VSampleToken is ERC20, Ownable {
    // 规则参数(使用更节省gas的数据类型)
    uint256 public constant DAILY_CLAIM_AMOUNT = 100 * 10**18;
    uint8 public constant DAILY_CLAIM_LIMIT = 2;
    uint256 public constant TOTAL_CLAIM_LIMIT = 1000 * 10**18;
    uint256 public constant MIN_RESERVE = 100000 * 10**18;
    uint256 public constant MAX_SUPPLY = 1000000 * 10**18;
    
    struct ClaimRecord {
        uint32 lastClaimDay;  
        uint8 dailyClaimCount;
        uint96 totalClaimed; 
    }
    
    mapping(address => ClaimRecord) public claimRecords;
    // 事件:成功领取
    event Claimed(address indexed user, uint256 amount);

    constructor() ERC20("VSampleToken", "VST") Ownable(msg.sender) {
        _mint(address(this), MAX_SUPPLY);
    }

    // 统一余额检查逻辑
    function _checkReserve() private view {
        require(
            balanceOf(address(this)) >= DAILY_CLAIM_AMOUNT + MIN_RESERVE,
            "Insufficient contract reserve"
        );
    }
    
    function claim() external {
        address user = msg.sender;
        _checkReserve(); 

        if (user == owner()) {
            _transfer(address(this), user, DAILY_CLAIM_AMOUNT);
            return;
        }

        ClaimRecord storage record = claimRecords[user];
        uint32 currentDay = uint32(block.timestamp / 1 days);
        
        // 新的一天重置计数器
        if (record.lastClaimDay != currentDay) {
            record.lastClaimDay = currentDay;
            record.dailyClaimCount = 0;
        }
        
        require(record.dailyClaimCount < DAILY_CLAIM_LIMIT, "Daily limit reached");
        require(
            record.totalClaimed + DAILY_CLAIM_AMOUNT <= TOTAL_CLAIM_LIMIT, 
            "Total claim limit exceeded"
        );

        // 更新记录(使用安全类型转换)
        record.dailyClaimCount += 1;
        record.totalClaimed += uint96(DAILY_CLAIM_AMOUNT);
        
        _transfer(address(this), user, DAILY_CLAIM_AMOUNT);
        emit Claimed(user, DAILY_CLAIM_AMOUNT);
    }
    
    function withdrawRemaining(address to, uint256 amount) external onlyOwner {
        require(
            balanceOf(address(this)) - amount >= MIN_RESERVE,
            "Cannot break reserve requirement"
        );
        _transfer(address(this), to, amount);
    }

    function getClaimStatus(address user) external view returns (
        bool canClaim,
        uint8 availableToday,
        uint256 remainingTotal
    ) {
        if (user == owner()) {
            return (
                balanceOf(address(this)) >= DAILY_CLAIM_AMOUNT + MIN_RESERVE,
                type(uint8).max,
                type(uint256).max
            );
        }

        ClaimRecord memory record = claimRecords[user];
        uint32 currentDay = uint32(block.timestamp / 1 days);
        
        availableToday = record.lastClaimDay == currentDay 
            ? DAILY_CLAIM_LIMIT - record.dailyClaimCount
            : DAILY_CLAIM_LIMIT;
            
        remainingTotal = TOTAL_CLAIM_LIMIT - record.totalClaimed;
        canClaim = (availableToday > 0) && 
                  (remainingTotal >= DAILY_CLAIM_AMOUNT) &&
                  (balanceOf(address(this)) >= DAILY_CLAIM_AMOUNT + MIN_RESERVE);
    }
}

 

posted @ 2025-08-06 14:40  daviyoung  阅读(18)  评论(0)    收藏  举报