The contract stores one bytes32. The list of eligible addresses lives off chain, and each claimant brings the few hashes needed to prove they were in it.
Walk the proof upward, hashing the smaller value first each time:
bytes32 hash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
hash = hash < proof[i]
? keccak256(abi.encodePacked(hash, proof[i]))
: keccak256(abi.encodePacked(proof[i], hash));
}
return hash == root;Sorting the pair is what lets the contract verify without knowing which side the leaf sat on.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Airdrop {
bytes32 public immutable root;
uint256 public constant AMOUNT = 100;
mapping(address => uint256) public balanceOf;
mapping(address => bool) public claimed;
event Claimed(address indexed who, uint256 amount);
constructor(bytes32 merkleRoot) {
root = merkleRoot;
}
// Rebuild the root from a leaf and the proof beside it.
function verify(bytes32[] calldata proof, bytes32 leaf) public view returns (bool) {
return false;
}
function claim(bytes32[] calldata proof) external {
balanceOf[msg.sender] += AMOUNT;
emit Claimed(msg.sender, AMOUNT);
}
}