-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMidasNFTMerkleDistributor.sol
70 lines (58 loc) · 2.66 KB
/
MidasNFTMerkleDistributor.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./interfaces/IMerkleDistributor.sol";
contract MerkleDistributor is IMerkleDistributor, IERC721Receiver {
address public immutable override token;
bytes32 public immutable override merkleRoot;
// This is a packed array of booleans.
mapping(uint256 => uint256) private claimedBitMap;
constructor(address token_, bytes32 merkleRoot_) public {
token = token_;
merkleRoot = merkleRoot_;
}
// Implementing `onERC721Received` so this contract can receive the Midas NFT
// Note that the operator is recorded as the owner of the Midas NFT
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata
)
external
override
returns (bytes4)
{
return IERC721Receiver.onERC721Received.selector;
}
function isClaimed(uint256 index) public view override returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedBitMap[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function _setClaimed(uint256 index) private {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
}
function _transferERC721FromContract(address account, uint256 tokenId) private returns (bool){
IERC721(token).transferFrom(address(this), account, tokenId);
return true;
}
// @dev tokenId should be a random number which could be generated by frontend,
// or it could be implemented by this contract if necessary.
function claim(uint256 index, address account, uint256 tokenId, uint256 amount, bytes32[] calldata merkleProof) external override {
require(!isClaimed(index), 'MerkleDistributor: Drop already claimed.');
// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(index, account, amount));
require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDistributor: Invalid proof.');
// Mark it claimed and send the token.
_setClaimed(index);
require(_transferERC721FromContract(account, tokenId), 'MerkleDistributor: Transfer failed.');
emit Claimed(index, account, tokenId);
}
}