-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathAirdropClaimSignature.ERC20.sol
90 lines (73 loc) · 3.97 KB
/
AirdropClaimSignature.ERC20.sol
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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// Solady
import {ERC20} from "@solady/tokens/ERC20.sol";
import {ECDSA} from "@solady/utils/ECDSA.sol";
import {Ownable} from "@solady/auth/Ownable.sol";
/// @title AirdropClaimSignatureERC20
/// @notice ERC20 claimable with signature
/// @dev Just an example - not audited
contract AirdropClaimSignatureERC20 is Ownable {
ERC20 public token;
/* -------------------------------------------------------------------------- */
/* ERRORS */
/* -------------------------------------------------------------------------- */
/// @dev The account has already claimed their tokens
error AirdropClaimSignature_AlreadyClaimed();
/// @dev The account has an invalid signature (malformed or nothing to claim)
error AirdropClaimSignature_InvalidSignature();
/// @dev The transfer of tokens failed
error AirdropClaimSignature_TransferFailed();
/* -------------------------------------------------------------------------- */
/* EVENTS */
/* -------------------------------------------------------------------------- */
/// @dev Emitted after a successful claim
/// @param recipient of the ERC20 tokens
/// @param amount of tokens claimed
event Claimed(address indexed recipient, uint256 amount);
/* -------------------------------------------------------------------------- */
/* STORAGE */
/* -------------------------------------------------------------------------- */
/* -------------------------------- IMMUTABLE ------------------------------- */
/// @dev Account that can sign messages to claim tokens
address public immutable signer;
/* ---------------------------------- STATE --------------------------------- */
/// @dev Whether account has claimed tokens already
mapping(address account => bool claimed) public hasClaimed;
/* -------------------------------------------------------------------------- */
/* CONSTRUCTOR */
/* -------------------------------------------------------------------------- */
/// @dev Initialize contract with tokens and merkle root
/// @param _token to be claimed (ERC20 compatible)
/// @param _signer of the claim messages
/// Note: Sets owner to deployer
constructor(ERC20 _token, address _signer) {
token = _token;
signer = _signer;
_initializeOwner(msg.sender);
}
/* -------------------------------------------------------------------------- */
/* FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @dev Claim tokens share with a valid signature
/// @param _recipient address of the target account
/// @param _amount of tokens to claim
/// @param _signature of the claim message
function claimERC20(address _recipient, uint256 _amount, bytes calldata _signature) external {
// Throw if address has already claimed tokens
if (hasClaimed[_recipient]) revert AirdropClaimSignature_AlreadyClaimed();
// Recover signer from signature
address recoveredSigner = ECDSA.recoverCalldata(
ECDSA.toEthSignedMessageHash(keccak256(abi.encodePacked(_recipient, _amount))), _signature
);
// Revert if recovered signer is not the signer
if (recoveredSigner != signer) revert AirdropClaimSignature_InvalidSignature();
// Mark account as claimed
hasClaimed[_recipient] = true;
// Transfer tokens to recipient
bool success = token.transfer(_recipient, _amount);
if (!success) revert AirdropClaimSignature_TransferFailed();
// Emit claim event
emit Claimed(_recipient, _amount);
}
}