-
Notifications
You must be signed in to change notification settings - Fork 215
/
Copy pathMerkleClaimERC20.sol
79 lines (60 loc) · 2.48 KB
/
MerkleClaimERC20.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
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// ============ Imports ============
import { ERC20 } from "@solmate/tokens/ERC20.sol"; // Solmate: ERC20
import { MerkleProof } from "@openzeppelin/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof
/// @title MerkleClaimERC20
/// @notice ERC20 claimable by members of a merkle tree
/// @author Anish Agnihotri <[email protected]>
/// @dev Solmate ERC20 includes unused _burn logic that can be removed to optimize deployment cost
contract MerkleClaimERC20 is ERC20 {
/// ============ Immutable storage ============
/// @notice ERC20-claimee inclusion root
bytes32 public immutable merkleRoot;
/// ============ Mutable storage ============
/// @notice Mapping of addresses who have claimed tokens
mapping(address => bool) public hasClaimed;
/// ============ Errors ============
/// @notice Thrown if address has already claimed
error AlreadyClaimed();
/// @notice Thrown if address/amount are not part of Merkle tree
error NotInMerkle();
/// ============ Constructor ============
/// @notice Creates a new MerkleClaimERC20 contract
/// @param _name of token
/// @param _symbol of token
/// @param _decimals of token
/// @param _merkleRoot of claimees
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals,
bytes32 _merkleRoot
) ERC20(_name, _symbol, _decimals) {
merkleRoot = _merkleRoot; // Update root
}
/// ============ Events ============
/// @notice Emitted after a successful token claim
/// @param to recipient of claim
/// @param amount of tokens claimed
event Claim(address indexed to, uint256 amount);
/// ============ Functions ============
/// @notice Allows claiming tokens if address is part of merkle tree
/// @param to address of claimee
/// @param amount of tokens owed to claimee
/// @param proof merkle proof to prove address and amount are in tree
function claim(address to, uint256 amount, bytes32[] calldata proof) external {
// Throw if address has already claimed tokens
if (hasClaimed[to]) revert AlreadyClaimed();
// Verify merkle proof, or revert if not in tree
bytes32 leaf = keccak256(abi.encodePacked(to, amount));
bool isValidLeaf = MerkleProof.verify(proof, merkleRoot, leaf);
if (!isValidLeaf) revert NotInMerkle();
// Set address to claimed
hasClaimed[to] = true;
// Mint tokens to address
_mint(to, amount);
// Emit claim event
emit Claim(to, amount);
}
}