122 lines
3.8 KiB
Solidity
122 lines
3.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.19;
|
|
|
|
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import {HasSignature} from "../core/HasSignature.sol";
|
|
import {TimeChecker} from "../utils/TimeChecker.sol";
|
|
|
|
contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
|
|
using SafeERC20 for IERC20;
|
|
|
|
uint256 public immutable _CACHED_CHAIN_ID;
|
|
address public immutable _CACHED_THIS;
|
|
address public verifier;
|
|
|
|
mapping(address token => address wallet) public erc20Wallets;
|
|
|
|
// store user's claimed status
|
|
mapping(address user => mapping(address token => uint256 claimedBit)) public claimedBitMap;
|
|
|
|
event EventERC20Wallet(address erc20, address wallet);
|
|
|
|
event EventVerifierUpdated(address indexed verifier);
|
|
event EventTokenClaimed(address indexed user, address indexed token, address account, uint256 amount, uint256 bit);
|
|
|
|
constructor(address _wallet, address _token, address _verifier, uint256 _duration) TimeChecker(_duration) {
|
|
_CACHED_CHAIN_ID = block.chainid;
|
|
_CACHED_THIS = address(this);
|
|
erc20Wallets[_token] = _wallet;
|
|
verifier = _verifier;
|
|
}
|
|
|
|
/**
|
|
* @dev update verifier address
|
|
*/
|
|
function updateVerifier(address _verifier) external onlyOwner {
|
|
require(_verifier != address(0), "TokenClaim: address can not be zero");
|
|
verifier = _verifier;
|
|
emit EventVerifierUpdated(_verifier);
|
|
}
|
|
|
|
/**
|
|
* @dev update pause state
|
|
*/
|
|
function pause() external onlyOwner {
|
|
_pause();
|
|
}
|
|
|
|
/**
|
|
* @dev update unpause state
|
|
*/
|
|
function unpause() external onlyOwner {
|
|
_unpause();
|
|
}
|
|
|
|
/**
|
|
* @dev update ERC20 wallet
|
|
*/
|
|
function updateERC20Wallet(address erc20, address wallet) external onlyOwner {
|
|
require(erc20Wallets[erc20] != wallet, "TokenClaim: ERC20 wallet not changed");
|
|
erc20Wallets[erc20] = wallet;
|
|
emit EventERC20Wallet(erc20, wallet);
|
|
}
|
|
|
|
/**
|
|
* @dev claim CEC with signature
|
|
* @param account address which eligible to claim, only for event log
|
|
* @param token address of token
|
|
* @param vals array of amount, bit, signTime, saltNonce
|
|
* @param signature signature of claim
|
|
*/
|
|
|
|
function claim(
|
|
address account,
|
|
address token,
|
|
uint256[4] calldata vals, // amount, bit, signTime, saltNonce
|
|
bytes calldata signature
|
|
) external signatureValid(signature) timeValid(vals[2]) nonReentrant whenNotPaused {
|
|
require(erc20Wallets[token] != address(0), "TokenClaim: token is not supported");
|
|
require(vals[0] > 0, "TokenClaim: amount is zero");
|
|
uint256 current = claimedBitMap[account][token];
|
|
require(current & vals[1] == 0, "TokenClaim: condition check failed");
|
|
address user = _msgSender();
|
|
bytes32 criteriaMessageHash = getMessageHash(
|
|
user,
|
|
account,
|
|
token,
|
|
_CACHED_THIS,
|
|
_CACHED_CHAIN_ID,
|
|
vals
|
|
);
|
|
checkSigner(verifier, criteriaMessageHash, signature);
|
|
_useSignature(signature);
|
|
claimedBitMap[account][token] = current | vals[1];
|
|
IERC20(token).safeTransferFrom(erc20Wallets[token], user, vals[0]);
|
|
emit EventTokenClaimed(user, token, account, vals[0], vals[1]);
|
|
}
|
|
|
|
function getMessageHash(
|
|
address _user,
|
|
address _account,
|
|
address _token,
|
|
address _contract,
|
|
uint256 _chainId,
|
|
uint256[4] calldata _vals
|
|
) public pure returns (bytes32) {
|
|
bytes memory encoded = abi.encodePacked(
|
|
_user,
|
|
_account,
|
|
_token,
|
|
_contract,
|
|
_chainId
|
|
);
|
|
for (uint256 i = 0; i < _vals.length; i++) {
|
|
encoded = bytes.concat(encoded, abi.encodePacked(_vals[i]));
|
|
}
|
|
return keccak256(encoded);
|
|
}
|
|
}
|