1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract GameAirdrop is Ownable, Pausable, ReentrancyGuard {
address public GAMETokenAddress; address public RecordOwner; bool public emergencyUnstakeFlag;
struct StakeInfo { uint256 stakeAmount; uint256 unstakeTimestamp; }
mapping(address => StakeInfo) internal _stakeRecords; uint256 internal _stakeTimestamp = 2 * 365 * 24 * 60 * 60;
event GameStake(address user, uint256 oldStakeAmount,uint256 newStakeAmount, uint256 oldUnstakeTimestamp,uint256 newUnstakeTimestamp);
event GameUnstake(address user, uint256 unstakeAmount, uint256 unstakeTimestamp);
event GameRecordStake(address user, uint256 stakeAmount, uint256 unstakeTimestamp);
modifier onlyEOA() { require(msg.sender.code.length == 0, "only EOA"); _; }
modifier onlyRecordOwner() { require(msg.sender == RecordOwner, "only RecordOwner"); _; }
constructor(address ownerAddress,address RecordOwner_,address GameTokenAddress_) Ownable(ownerAddress) { GAMETokenAddress = GameTokenAddress_; RecordOwner = RecordOwner_; emergencyUnstakeFlag = false;
} function updateEmergeceUnstakeFlag(bool flag) public onlyOwner { emergencyUnstakeFlag = flag; }
function getUserStakeInfo(address user) public view returns(StakeInfo memory) { StakeInfo memory stakeRecord = _stakeRecords[user]; return stakeRecord; }
function pause() public onlyOwner { _pause(); }
function unpause() public onlyOwner { _unpause(); }
function RecordStake(address[] memory recordAddress,StakeInfo[] memory recordInfos) public onlyRecordOwner{ for (uint256 i = 0; i < recordAddress.length; i++) { StakeInfo memory stakeRecord = recordInfos[i]; _stakeRecords[recordAddress[i]] = stakeRecord; emit GameRecordStake(recordAddress[i],stakeRecord.stakeAmount,stakeRecord.unstakeTimestamp); } }
function renounceRecordOwner() public onlyRecordOwner { RecordOwner = address(0); }
function stake(uint256 amount) public nonReentrant whenNotPaused onlyEOA {
StakeInfo storage stakeRecord = _stakeRecords[msg.sender]; uint256 oldAmount = stakeRecord.stakeAmount; uint256 oldUnstakeTimestamp = stakeRecord.unstakeTimestamp; uint256 newUnstakeTimestamp = block.timestamp + _stakeTimestamp; require(amount > 0, "invalid amount"); IERC20(GAMETokenAddress).transferFrom(msg.sender, address(this), amount); stakeRecord.stakeAmount += amount; stakeRecord.unstakeTimestamp = newUnstakeTimestamp; emit GameStake(msg.sender, oldAmount, stakeRecord.stakeAmount, oldUnstakeTimestamp, stakeRecord.unstakeTimestamp); }
function queryUnstake(address user) public view returns(uint256 userAmount, uint256 unstakeTimestamp) { StakeInfo memory stakeRecord = _stakeRecords[user]; userAmount = stakeRecord.stakeAmount; unstakeTimestamp = stakeRecord.unstakeTimestamp; }
function unstake() public nonReentrant whenNotPaused { StakeInfo storage stakeRecord = _stakeRecords[msg.sender]; uint256 unstakeAmount = stakeRecord.stakeAmount; require(stakeRecord.stakeAmount > 0, "not stake yet"); require(block.timestamp > stakeRecord.unstakeTimestamp, "invalid timestamp"); IERC20(GAMETokenAddress).transfer(msg.sender, unstakeAmount); stakeRecord.stakeAmount = 0; stakeRecord.unstakeTimestamp = 0; emit GameUnstake(msg.sender, unstakeAmount, block.timestamp); }
function emergencyUnstake() public nonReentrant whenNotPaused { require(emergencyUnstakeFlag, "emergency unstake is not allowed"); StakeInfo storage stakeRecord = _stakeRecords[msg.sender]; uint256 unstakeAmount = stakeRecord.stakeAmount; require(stakeRecord.stakeAmount > 0, "not stake yet"); IERC20(GAMETokenAddress).transfer(msg.sender, unstakeAmount); stakeRecord.stakeAmount = 0; stakeRecord.unstakeTimestamp = 0; emit GameUnstake(msg.sender, unstakeAmount, block.timestamp); } }
|