当前位置:   article > 正文

Solidity学习之路 - 一步一步写一个空投合约_批量空投合约

批量空投合约
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity >=0.8.0;
  3. import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
  4. import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
  5. import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
  6. contract Airdropper {
  7. using SafeERC20 for IERC20;
  8. address private owner;
  9. uint256 public airdropPerAmount; // 每人每次可以领取的空投数量
  10. uint256 public airdropTotalAmount; // 每人可以领取的空投总数
  11. uint256 public airdropTerm; // 多久可以领取一次(单位s),未设置则每人只能领一次
  12. uint256 public airdropStartTime; // 空投开始时间(时间戳)
  13. uint256 public airdropDeadline; // 空投截止时间(时间戳)
  14. address public tokenAddress; // 空投token地址
  15. mapping(address => uint256) public airdropRecord; // 每个人空投领取总额
  16. mapping(address => uint256) public airdropTimeRecord; // 最后一次领取空投时间
  17. // 发布合约时需传入5个参数,注意精度问题
  18. constructor(uint256 _airdropPerAmount, uint256 _airdropTotalAmount, uint256 _airdropTerm, uint256 _airdropStartTime, uint256 _airdropDeadline) {
  19. owner = msg.sender;
  20. airdropPerAmount = _airdropPerAmount;
  21. airdropTotalAmount = _airdropTotalAmount;
  22. airdropTerm = _airdropTerm;
  23. airdropStartTime = _airdropStartTime;
  24. airdropDeadline = _airdropDeadline;
  25. }
  26. // 批量发放空投,dests和values两个数组长度若相等,则给不同地址发放对应数量token,如果values只有一个元素,则每个地址发放等量token
  27. function doAirdrop(address[] memory dests, uint256[] memory values) external virtual returns (uint256) {
  28. // 批量发放一般为官方操作,不受时间、领取额度等以上各种条件限制
  29. require(msg.sender == owner, 'Airdropper: forbidden');
  30. require(tokenAddress != address(0), 'Airdropper: address not zero');
  31. uint256 i = 0;
  32. while (i < dests.length) {
  33. uint sendAmount = values.length == 1 ? values[0] : values[i];
  34. // 判断当前合约中剩余token是否够发放数量,如果不够则结束发放并返回已发放的最后一个索引
  35. if(ERC20(tokenAddress).balanceOf(address(this)) < sendAmount){
  36. break;
  37. }
  38. // 接收地址不为0,发放数量不为0,则执行发放
  39. if(dests[i] != address(0) && sendAmount > 0){
  40. IERC20(tokenAddress).safeTransfer(dests[i], sendAmount);
  41. }
  42. i++;
  43. }
  44. return i;
  45. }
  46. // 个人领取空投
  47. function getAirdrop() external virtual returns(bool){
  48. // token地址不能为0地址
  49. require(tokenAddress != address(0), 'Airdropper: address not zero');
  50. // 每人每次可以领取的空投数量要大于0
  51. require(airdropPerAmount > 0, 'Airdropper: no parameter set');
  52. // 当前时间要大于空投开始时间
  53. require(block.timestamp >= airdropStartTime, 'Airdropper: not started');
  54. if(airdropTotalAmount > 0){
  55. // 如果设置了 每人可以领取的空投总数 这个参数,则验证已领取数量要小于这个总数
  56. require(airdropRecord[msg.sender] < airdropTotalAmount, 'Airdropper: total amount limit');
  57. }
  58. if (airdropTerm > 0) {
  59. // 如果设置了领取周期参数,则验证当前时间减去上次领取时间大于这个周期
  60. require(block.timestamp - airdropTimeRecord[msg.sender] > airdropTerm , 'Airdropper: term limit');
  61. } else {
  62. // 如果没有设置周期参数,则验证没有领取过可以领取,只能领1
  63. require(airdropRecord[msg.sender] == 0, 'Airdropper: you have already received');
  64. }
  65. if (airdropDeadline > 0) {
  66. // 如果设置了空投截止时间,则验证当前时间小于截止时间
  67. require(airdropDeadline > block.timestamp, 'Airdropper: deadline');
  68. }
  69. // 验证当前合约token数量够发放数量
  70. require(ERC20(tokenAddress).balanceOf(address(this)) >= airdropPerAmount, 'Airdropper: insufficient assets');
  71. // 执行发放
  72. IERC20(tokenAddress).safeTransfer(msg.sender, airdropPerAmount);
  73. // 累计领取总数
  74. airdropRecord[msg.sender] += airdropPerAmount;
  75. // 记录最后领取时间
  76. airdropTimeRecord[msg.sender] = block.timestamp;
  77. return true;
  78. }
  79. // 充入token
  80. function recharge(address _tokenAddress, uint256 _amount) external virtual returns(bool) {
  81. require(msg.sender == owner, 'Airdropper: forbidden');
  82. require(_tokenAddress != address(0), 'Airdropper: forbidden');
  83. if(tokenAddress == address(0)){
  84. // 第一次充入,配置token地址
  85. tokenAddress = _tokenAddress;
  86. } else {
  87. // 否则验证充入的token和配置的地址一致
  88. require(_tokenAddress == tokenAddress, 'Airdropper: Error token address');
  89. }
  90. // 执行充入token
  91. IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
  92. return true;
  93. }
  94. // 提出剩余token
  95. function withdraw() external virtual returns(bool) {
  96. require(msg.sender == owner, 'Airdropper: forbidden');
  97. require(tokenAddress != address(0), 'Airdropper: address not zero');
  98. // 将剩余token全部转给合约发布者
  99. IERC20(tokenAddress).safeTransfer(owner, ERC20(tokenAddress).balanceOf(address(this)));
  100. tokenAddress = address(0); // 重置token地址
  101. return true;
  102. }
  103. /**
  104. * 以下是配置各个参数的接口,只有合约发布者可以调用
  105. */
  106. function setPerAmount(uint256 _airdropPerAmount) external virtual returns(bool) {
  107. require(msg.sender == owner, 'Airdropper: forbidden');
  108. airdropPerAmount = _airdropPerAmount;
  109. return true;
  110. }
  111. function setTotalAmount(uint256 _airdropTotalAmount) external virtual returns(bool) {
  112. require(msg.sender == owner, 'Airdropper: forbidden');
  113. airdropTotalAmount = _airdropTotalAmount;
  114. return true;
  115. }
  116. function setTerm(uint256 _airdropTerm) external virtual returns(bool) {
  117. require(msg.sender == owner, 'Airdropper: forbidden');
  118. airdropTerm = _airdropTerm;
  119. return true;
  120. }
  121. function setStartTime(uint256 _airdropStartTime) external virtual returns(bool) {
  122. require(msg.sender == owner, 'Airdropper: forbidden');
  123. airdropStartTime = _airdropStartTime;
  124. return true;
  125. }
  126. function setDeadline(uint256 _airdropDeadline) external virtual returns(bool) {
  127. require(msg.sender == owner, 'Airdropper: forbidden');
  128. airdropDeadline = _airdropDeadline;
  129. return true;
  130. }
  131. }

这是一个经过实践的空投合约,在bsc和matic可以直接使用,在trc上问题应该也不大。很高兴分享给大家。在一些审计比较严格的情况下会报warning,每个函数都要有Event,自己加上就行。提示词就略过吧,实在懒得翻译,用的时候自己换一个自己喜欢的就好。

后续还会不断发布各类经过审计且完整可用的合约模板。现在正在整理过往项目,准备开发一个通用的Dapp快速开发框架。

加关注,不迷路,很开心和大家一起交流学习心得。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/369168
推荐阅读
相关标签
  

闽ICP备14008679号