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

推荐订阅源

U
Unit 42
S
Securelist
小众软件
小众软件
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 司徒正美
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
O
OpenAI News
Cloudbric
Cloudbric
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
MongoDB | Blog
MongoDB | Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
V2EX
PCI Perspectives
PCI Perspectives
T
Troy Hunt's Blog
Schneier on Security
Schneier on Security
P
Palo Alto Networks Blog
M
MIT News - Artificial intelligence
V2EX - 技术
V2EX - 技术
阮一峰的网络日志
阮一峰的网络日志
Hacker News - Newest:
Hacker News - Newest: "LLM"
G
Google Developers Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Last Watchdog
The Last Watchdog
The Register - Security
The Register - Security
腾讯CDC
N
News and Events Feed by Topic
C
Check Point Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
Webroot Blog
Webroot Blog
P
Proofpoint News Feed
S
Schneier on Security
MyScale Blog
MyScale Blog
N
News | PayPal Newsroom
Recorded Future
Recorded Future
T
Tenable Blog
I
InfoQ
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Microsoft Security Blog
Microsoft Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
Engineering at Meta
Engineering at Meta

博客园 - 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);
    }
}