Helpful?
DonaSwapTokenMigrator.sol
Code
0x6647878949C04E92B5285C8B69eF9f489B0E0622
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
using SafeERC20 for IERC20;
/**
* @title DonaSwapTokenMigrator
* @dev This contract allows users to claim tokens based on provided vouchers.
* It implements the Ownable pattern, ensuring that only the contract owner
* can perform certain operations such as setting the signer address and
* managing claimed tokens. Additionally, it utilizes EIP712 for signature
* verification to ensure the validity of vouchers.
*/
contract DonaSwapTokenMigrator is Ownable(msg.sender), EIP712 {
IERC20 public token;
mapping(address => bool) public hasClaimed;
address public signer = address(0x29E4711B8b2FDAFf30f70372a98A9Ad9f5A91cfa);
string private constant SIGNING_DOMAIN = "TokenDrop-Voucher";
string private constant SIGNATURE_VERSION = "1";
// Struct representing a TokenDropVoucher
struct TokenDropVoucher {
uint256 amount;
uint256 expires;
address wallet;
/// @notice the EIP-712 signature of all other fields in the struct.
/// For a voucher to be valid, it must be signed by an account with the signer.
bytes signature;
}
// Constructor initializing the contract with the specified token
constructor(IERC20 _token) EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {
token = _token;
}
// Internal function to hash the TokenDropVoucher struct
function _hash(TokenDropVoucher memory voucher) internal view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(
keccak256("Struct(uint256 amount,uint256 expires,address wallet)"),
voucher.amount,
voucher.expires,
voucher.wallet
)));
}
// Internal function to verify the signature of a TokenDropVoucher
function _verify(TokenDropVoucher memory voucher) internal view returns (address) {
bytes32 digest = _hash(voucher);
return ECDSA.recover(digest, voucher.signature);
}
// Function to set a new signer address, accessible only by the contract owner
function setSigner(address _signer) public onlyOwner {
signer = _signer;
}
// Function to unset the claim status of a wallet, accessible only by the contract owner
function unsetClaim(address wallet) public onlyOwner {
hasClaimed[wallet] = false;
}
// Function to claim tokens based on a voucher, requires a valid signature from the signer
function claimTokens(uint256 amount, uint256 expires, address wallet, bytes memory signature) external {
require(!hasClaimed[wallet], "Tokens already claimed");
require(block.number <= expires, "Voucher expired");
TokenDropVoucher memory voucher = TokenDropVoucher({amount: amount, expires: expires, wallet: wallet, signature: signature});
address _signer = _verify(voucher);
require(_signer == signer, "Invalid signature");
hasClaimed[voucher.wallet] = true;
token.safeTransfer(voucher.wallet, voucher.amount);
}
// Function to withdraw ERC20 tokens, accessible only by the contract owner
function withdrawERC20(IERC20 _token, address to, uint256 amount) public onlyOwner {
uint256 erc20balance = _token.balanceOf(address(this));
require(amount <= erc20balance, "Balance is too low");
require(amount > 0, "Balance must be higher than zero");
if (amount == 0) {
amount = erc20balance;
}
_token.transfer(to, amount);
}
// Function to withdraw native assets, accessible only by the contract owner
function withdraw() public onlyOwner {
address payable receiver = payable(msg.sender);
uint256 _balance = address(this).balance;
require(_balance > 0, "Balance must be higher than zero");
receiver.transfer(_balance);
}
}