i'm working at a smart-contract that has a merkle-tree function in it.
Basically i'm passing the proof from a firebase function that take in input the address and the n° of token reserved by the user, and gives as output the proof to the front-end that execute the smart-contract function.
The only thing i need to figure out is the leaf calculation, on the firebase function it is composed by encoding keccak256 the string in the array, composed by an eth address concatenated with the number of reserved tokens (0x38207d0e84edb04a57482c3769fe192617520373 + 3), in the smart contract instead, the leaf is calculated with the keccak256(abi.encodepacked(0x38207d0e84edb04a57482c3769fe1926175203733, 3), and it's not passing the verifiy...
I think these two methods of calculation are different in some part, can you help me?
I'm going to paste the code of both functions:
SOLIDITY
// 1. Address check, if the proof is valid
function isPrivateListed(
uint256 _addressReservedTokens,
bytes32[] memory _proof
) public view returns (bool) {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _addressReservedTokens);
return MerkleProof.verify(_proof, privateListRoot, leaf);
}
// 2. Mint function called from the front-end
function privateMint(
uint256 _mintAmount,
uint256 _addressReservedTokens,
bytes32[] calldata proof
) public payable {
require(privateOpen, "The private mint is not opened");
require(
isPrivateListed(_addressReservedTokens, proof),
"you are not in the private list"
);
uint256 supply = totalSupply();
require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");
uint256 owned = addressMintedBalance[msg.sender];
require(
owned + _mintAmount <= _addressReservedTokens,
"You have less nft reserved"
);
for (uint256 i = 1; i <= _mintAmount; i++) {
_safeMint(msg.sender, supply + i);
addressMintedBalance[msg.sender]++;
}
}
FIREBASE FUNCTION
// 1. Get parameters from the function called from client side
const walletAddress = data.walletAddress;
let whitelistAddresses = [
'0x38207d0e84edb04a57482c3769fe1926175203733',
'0x943926a8ff0000350d0b879a658fa52bcd4fca186',
];
// 2. Create a new array of `leafNodes` by hashing all indexes of the `whitelistAddresses`
// using `keccak256`. Then creates a Merkle Tree object using keccak256 as the algorithm.
const leafNodes = whitelistAddresses.map((addr) => keccak256(addr));
const merkleTree = new MerkleTree(leafNodes, keccak256, {
sortPairs: true,
});
// 3. Get root hash of the `merkleeTree` in hexadecimal format (0x)
const rootHash = merkleTree.getRoot();
const rootHashHex = merkleTree.getHexRoot();
// ***** ***** ***** CLIENT-SIDE ***** ***** ***** //
const claimingAddress = keccak256(walletAddress);
const hexProof = merkleTree.getHexProof(claimingAddress);
const verifyProof = merkleTree.verify(hexProof, claimingAddress, rootHash);
In solidity you're hashing a (20+32)-bytes element
Since
msg.senderis 20 bytes and_addressReservedTokensis 32 bytes, the output ofabi.encodePacked()is something like this:This is clearly different than
0x38207d0e84edb04a57482c3769fe1926175203733used by firebase.Also I'm not sure if the js keccak256 is interpreting that value as string or as bytes (in solidity it's bytes).
The best fix is probably changing the js code. You can write the correct
whitelistAddressesusing ethers, see here https://ethereum.stackexchange.com/questions/119990/how-to-mimic-abi-encodepacked-in-ethers