文件 1 的 1:ClaimToken.sol
pragma solidity ^0.8.0;
library ECDSA {
function recover(bytes32 hash, bytes memory signature)
internal
pure
returns (address)
{
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return recover(hash, r, vs);
} else {
revert("ECDSA: invalid signature length");
}
}
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
bytes32 s;
uint8 v;
assembly {
s := and(
vs,
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
)
v := add(shr(255, vs), 27)
}
return recover(hash, v, r, s);
}
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
require(
uint256(s) <=
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
"ECDSA: invalid signature 's' value"
);
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
function toEthSignedMessageHash(bytes32 hash)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
);
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\x19\x01", domainSeparator, structHash)
);
}
}
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface IERC1363 is IERC20, IERC165 {
function transferAndCall(address to, uint256 value) external returns (bool);
function transferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool);
function transferFromAndCall(
address from,
address to,
uint256 value
) external returns (bool);
function transferFromAndCall(
address from,
address to,
uint256 value,
bytes calldata data
) external returns (bool);
function approveAndCall(address spender, uint256 value)
external
returns (bool);
function approveAndCall(
address spender,
uint256 value,
bytes calldata data
) external returns (bool);
}
library Errors {
error InsufficientBalance(uint256 balance, uint256 needed);
error FailedCall();
error FailedDeployment();
error MissingPrecompile(address);
}
library Address {
error AddressEmptyCode(address target);
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
function functionCall(address target, bytes memory data)
internal
returns (bytes memory)
{
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(
data
);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data)
internal
view
returns (bytes memory)
{
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data)
internal
returns (bytes memory)
{
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata)
internal
pure
returns (bytes memory)
{
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
library SafeERC20 {
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(
address spender,
uint256 currentAllowance,
uint256 requestedDecrease
);
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(
token,
abi.encodeCall(token.transferFrom, (from, to, value))
);
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 requestedDecrease
) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(
spender,
currentAllowance,
requestedDecrease
);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
bytes memory approvalCall = abi.encodeCall(
token.approve,
(spender, value)
);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(
token,
abi.encodeCall(token.approve, (spender, 0))
);
_callOptionalReturn(token, approvalCall);
}
}
function transferAndCallRelaxed(
IERC1363 token,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
function approveAndCallRelaxed(
IERC1363 token,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(
gas(),
token,
0,
add(data, 0x20),
mload(data),
0,
0x20
)
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (
returnSize == 0 ? address(token).code.length == 0 : returnValue != 1
) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data)
private
returns (bool)
{
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(
gas(),
token,
0,
add(data, 0x20),
mload(data),
0,
0x20
)
returnSize := returndatasize()
returnValue := mload(0)
}
return
success &&
(
returnSize == 0
? address(token).code.length > 0
: returnValue == 1
);
}
}
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
contract ClaimToken is Ownable {
using SafeERC20 for IERC20;
using ECDSA for bytes32;
address public signer;
constructor(address _owner, address pax) Ownable(_owner) {
PaxAddress = pax;
}
address public PaxAddress;
mapping(uint256 => bool) public ids;
event TokenClaimed(
address tokenAddress,
address user,
uint256 id,
uint256 amount
);
function getClaimMessageHash(
address tokenAddress,
address user,
uint256 id,
uint256 amount,
uint256 expiredAt
) public pure returns (bytes32) {
return
keccak256(
abi.encodePacked(tokenAddress, user, id, amount, expiredAt)
);
}
function balanceOf(address tokenAddress) public view returns (uint256) {
IERC20 token = IERC20(tokenAddress);
return token.balanceOf(address(this));
}
function claimToken(
uint256 id,
uint256 amount,
uint256 expiredAt,
bytes calldata signature
) external {
verifyClaimTokenSignature(
PaxAddress,
msg.sender,
id,
amount,
expiredAt,
signature
);
IERC20 token = IERC20(PaxAddress);
require(amount > 0, "ClaimToken: amout must be greater than 0");
require(!ids[id], "ClaimToken: the id is used");
require(
block.timestamp < expiredAt,
"ClaimToken: the signature is expired"
);
require(
token.balanceOf(address(this)) >= amount,
"ClaimToken: not sufficient tokens"
);
token.transfer(msg.sender, amount);
ids[id] = true;
emit TokenClaimed(PaxAddress, msg.sender, id, amount);
}
function setSigner(address _signer) external onlyOwner {
require(_signer != address(0), 'ClaimToken: invalid signer address');
signer = _signer;
}
function verifyClaimTokenSignature(
address tokenAddress,
address sender,
uint256 id,
uint256 amount,
uint256 expiredAt,
bytes calldata signature
) internal view {
bytes32 criteriaMessageHash = getClaimMessageHash(
tokenAddress,
sender,
id,
amount,
expiredAt
);
bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(
criteriaMessageHash
);
require(
ECDSA.recover(ethSignedMessageHash, signature) == signer,
"ClaimToken: invalid signature"
);
}
function withdrawBNB() public onlyOwner {
(bool os, ) = payable(owner()).call{value: address(this).balance}("");
require(os);
}
function withdrawTokens(IERC20 token) public onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.transfer(msg.sender, balance);
}
function withdrawManualy(address tokenAddress, uint256 amount)
external
onlyOwner
{
IERC20 token = IERC20(tokenAddress);
token.transfer(msg.sender, amount);
}
}