赞
踩
- // SPDX-License-Identifier: MIT
- pragma solidity >=0.8.0;
-
- import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
- import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
- import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
-
- contract Airdropper {
- using SafeERC20 for IERC20;
-
- address private owner;
- uint256 public airdropPerAmount; // 每人每次可以领取的空投数量
- uint256 public airdropTotalAmount; // 每人可以领取的空投总数
- uint256 public airdropTerm; // 多久可以领取一次(单位s),未设置则每人只能领一次
- uint256 public airdropStartTime; // 空投开始时间(时间戳)
- uint256 public airdropDeadline; // 空投截止时间(时间戳)
- address public tokenAddress; // 空投token地址
- mapping(address => uint256) public airdropRecord; // 每个人空投领取总额
- mapping(address => uint256) public airdropTimeRecord; // 最后一次领取空投时间
-
- // 发布合约时需传入5个参数,注意精度问题
- constructor(uint256 _airdropPerAmount, uint256 _airdropTotalAmount, uint256 _airdropTerm, uint256 _airdropStartTime, uint256 _airdropDeadline) {
- owner = msg.sender;
- airdropPerAmount = _airdropPerAmount;
- airdropTotalAmount = _airdropTotalAmount;
- airdropTerm = _airdropTerm;
- airdropStartTime = _airdropStartTime;
- airdropDeadline = _airdropDeadline;
- }
-
- // 批量发放空投,dests和values两个数组长度若相等,则给不同地址发放对应数量token,如果values只有一个元素,则每个地址发放等量token
- function doAirdrop(address[] memory dests, uint256[] memory values) external virtual returns (uint256) {
- // 批量发放一般为官方操作,不受时间、领取额度等以上各种条件限制
- require(msg.sender == owner, 'Airdropper: forbidden');
- require(tokenAddress != address(0), 'Airdropper: address not zero');
- uint256 i = 0;
- while (i < dests.length) {
- uint sendAmount = values.length == 1 ? values[0] : values[i];
- // 判断当前合约中剩余token是否够发放数量,如果不够则结束发放并返回已发放的最后一个索引
- if(ERC20(tokenAddress).balanceOf(address(this)) < sendAmount){
- break;
- }
- // 接收地址不为0,发放数量不为0,则执行发放
- if(dests[i] != address(0) && sendAmount > 0){
- IERC20(tokenAddress).safeTransfer(dests[i], sendAmount);
- }
-
- i++;
- }
- return i;
- }
-
- // 个人领取空投
- function getAirdrop() external virtual returns(bool){
- // token地址不能为0地址
- require(tokenAddress != address(0), 'Airdropper: address not zero');
- // 每人每次可以领取的空投数量要大于0
- require(airdropPerAmount > 0, 'Airdropper: no parameter set');
- // 当前时间要大于空投开始时间
- require(block.timestamp >= airdropStartTime, 'Airdropper: not started');
- if(airdropTotalAmount > 0){
- // 如果设置了 每人可以领取的空投总数 这个参数,则验证已领取数量要小于这个总数
- require(airdropRecord[msg.sender] < airdropTotalAmount, 'Airdropper: total amount limit');
- }
- if (airdropTerm > 0) {
- // 如果设置了领取周期参数,则验证当前时间减去上次领取时间大于这个周期
- require(block.timestamp - airdropTimeRecord[msg.sender] > airdropTerm , 'Airdropper: term limit');
- } else {
- // 如果没有设置周期参数,则验证没有领取过可以领取,只能领1次
- require(airdropRecord[msg.sender] == 0, 'Airdropper: you have already received');
- }
- if (airdropDeadline > 0) {
- // 如果设置了空投截止时间,则验证当前时间小于截止时间
- require(airdropDeadline > block.timestamp, 'Airdropper: deadline');
- }
- // 验证当前合约token数量够发放数量
- require(ERC20(tokenAddress).balanceOf(address(this)) >= airdropPerAmount, 'Airdropper: insufficient assets');
- // 执行发放
- IERC20(tokenAddress).safeTransfer(msg.sender, airdropPerAmount);
- // 累计领取总数
- airdropRecord[msg.sender] += airdropPerAmount;
- // 记录最后领取时间
- airdropTimeRecord[msg.sender] = block.timestamp;
-
- return true;
- }
-
- // 充入token
- function recharge(address _tokenAddress, uint256 _amount) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- require(_tokenAddress != address(0), 'Airdropper: forbidden');
- if(tokenAddress == address(0)){
- // 第一次充入,配置token地址
- tokenAddress = _tokenAddress;
- } else {
- // 否则验证充入的token和配置的地址一致
- require(_tokenAddress == tokenAddress, 'Airdropper: Error token address');
- }
- // 执行充入token
- IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
-
- return true;
- }
-
- // 提出剩余token
- function withdraw() external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- require(tokenAddress != address(0), 'Airdropper: address not zero');
- // 将剩余token全部转给合约发布者
- IERC20(tokenAddress).safeTransfer(owner, ERC20(tokenAddress).balanceOf(address(this)));
- tokenAddress = address(0); // 重置token地址
-
- return true;
- }
-
- /**
- * 以下是配置各个参数的接口,只有合约发布者可以调用
- */
- function setPerAmount(uint256 _airdropPerAmount) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- airdropPerAmount = _airdropPerAmount;
-
- return true;
- }
-
- function setTotalAmount(uint256 _airdropTotalAmount) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- airdropTotalAmount = _airdropTotalAmount;
-
- return true;
- }
-
- function setTerm(uint256 _airdropTerm) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- airdropTerm = _airdropTerm;
-
- return true;
- }
-
- function setStartTime(uint256 _airdropStartTime) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- airdropStartTime = _airdropStartTime;
-
- return true;
- }
-
- function setDeadline(uint256 _airdropDeadline) external virtual returns(bool) {
- require(msg.sender == owner, 'Airdropper: forbidden');
- airdropDeadline = _airdropDeadline;
-
- return true;
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这是一个经过实践的空投合约,在bsc和matic可以直接使用,在trc上问题应该也不大。很高兴分享给大家。在一些审计比较严格的情况下会报warning,每个函数都要有Event,自己加上就行。提示词就略过吧,实在懒得翻译,用的时候自己换一个自己喜欢的就好。
后续还会不断发布各类经过审计且完整可用的合约模板。现在正在整理过往项目,准备开发一个通用的Dapp快速开发框架。
加关注,不迷路,很开心和大家一起交流学习心得。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。