I'm trying to verify a leaf on a Merkle tree. I've generated the tree off-chain with merkletree.js using some "passwords" that I want to verify on-chain using OpenZeppelin MerkleProof library.
The problem is I got "No valid proof to claim an award" response even if the leaf is valid. My code:
The js script creating the tree and running the test
const assert = require('assert');
const ganache = require('ganache');
const { MerkleTree } = require('merkletreejs')
const keccak256 = require('keccak256');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const { abi, bytecode } = require('../build/contracts/LotteryNftAward.json');
let lottery;
let accounts;
const TOTAL_SUPPLY = 5;
const LEAVES = ["test", "merkletree", "lottery", "javascript", "blockchain", "factory"].map(p => keccak256(p));
const TREE = new MerkleTree(LEAVES, keccak256, { sortPairs: true });
const ROOT = TREE.getHexRoot();
const PROOF = TREE.getHexProof(LEAVES[1]);
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
lottery = await new web3.eth.Contract(abi)
.deploy({ data: bytecode, arguments: [TOTAL_SUPPLY, ROOT]})
.send({ from: accounts[0], gas: 4000000 });
});
describe('Lottery Contract', () => {
it('Invalid Proof', async () => {
try{
await lottery.methods.claim(PROOF, "wrongLeaf").send({
from: accounts[1]
});
assert(false);
} catch (err){
assert(err);
}
});
it('Valid Proof', async () => {
await lottery.methods.claim(PROOF, keccak256("merkletree")).send({
from: accounts[1]
});
});
});
The smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract AciLotteryNftAward is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
struct Player {
address playerAddress;
bool hasWon;
}
event Subscription(address newPlayer, uint256 totalSubscription);
uint256 immutable TOTAL_SUPPLY;
Player[] private players;
bytes32 immutable private MERKLE_ROOT;
mapping(address => bool) private claimTracker;
constructor(uint256 totalSupply, bytes32 merkleRoot) ERC721("AciLotteryNftAward", "ALNA") {
TOTAL_SUPPLY = totalSupply;
MERKLE_ROOT = merkleRoot;
}
.... other methods ....
function claim(bytes32[] calldata proof, bytes32 leaf) external {
//function claim() external {
uint256 currentTokenId = _tokenIdCounter.current();
require(currentTokenId < TOTAL_SUPPLY, "No more tokens to claim");
require(!claimTracker[msg.sender], "This address has already claimed");
require(MerkleProof.verifyCalldata(
proof,
MERKLE_ROOT,
keccak256(abi.encodePacked(leaf))
),
"No valid proof to claim an award");
_tokenIdCounter.increment();
claimTracker[msg.sender] = true;
_safeMint(msg.sender, _tokenIdCounter.current());
}
}
Best Answer
You might need to use
MerkleProof.verify()
method, as you're not using calldata while creating the Merkle Tree.Try This: