惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
Hacker News: Ask HN
Hacker News: Ask HN
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
G
GRAHAM CLULEY
W
WeLiveSecurity
H
Heimdal Security Blog
S
Secure Thoughts
L
Lohrmann on Cybersecurity
A
Arctic Wolf
N
News and Events Feed by Topic
Spread Privacy
Spread Privacy
S
Securelist
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tor Project blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
C
CERT Recently Published Vulnerability Notes
大猫的无限游戏
大猫的无限游戏
Project Zero
Project Zero
Google Online Security Blog
Google Online Security Blog
博客园_首页
博客园 - 叶小钗
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cloudbric
Cloudbric
T
The Blog of Author Tim Ferriss
云风的 BLOG
云风的 BLOG
Cyberwarzone
Cyberwarzone
IT之家
IT之家
Help Net Security
Help Net Security
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
小众软件
小众软件
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
V2EX - 技术
V2EX - 技术
H
Help Net Security
Simon Willison's Weblog
Simon Willison's Weblog
Stack Overflow Blog
Stack Overflow Blog
Cisco Talos Blog
Cisco Talos Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
NISL@THU
NISL@THU
雷峰网
雷峰网

博客园 - daviyoung

Windows 11 22H2 安装 .NET Framework 3.5 完整教程 System.Threading.Timer 详细讲解 Agent 开发入门(一):从零构建你的第一个智能体 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(五)表达式求值 python使用plotly绘制图表 手把手搭建OPC UA服务器 图像处理库Pillow的使用:批量裁剪图片 python-docx库的使用:图片插入到word文档里 modbus(二)用NModbus4库实现Modbus tcp从站 Jenkins 容器化实践:Docker 部署与 CI/CD 流水线配置 Streamlit实战 用pycdc批量反编译pyc文件 以ENS 的 BaseRegistrarImplementation 合约为例,用web3.py调用合约 虚拟环境下安装包后,vs code仍然有下滑波浪线及显示找不到包(运行是正常的)的解决办法 Merkle Tree 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(四)可视化 语法树 Solidity开发ERC20智能合约demo及部署到测试网 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(三)表达式的抽象语法树设计(Expr) 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(二)词法分析器
Solidity开发ERC20智能合约claim token的功能
daviyoung · 2025-08-06 · via 博客园 - daviyoung
// 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);
    }
}