Here is my js code of generating hex proof for solidity verification
const express = require('express')
const { MerkleTree } = require('merkletreejs');
const { bufferToHex } = require('ethereumjs-util');
const keccak256 = require('keccak256');
const url = require('url');
const cors = require("cors");
const app = express();
app.use(cors())
//1_000_000_000_000e18
//10000000000000000000000000000
let whitelist = [
{addr: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", amount: 20000000000000000000000000}, //10_000_000_000e18
{addr: "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", amount: 20000000000000000000000000},
]
const allLower = whitelist.map(item => item)
const leafNodes = allLower.map(item => keccak256(item.addr, item.amount))
console.log('leafNodes: ', leafNodes)
const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true })
function getProof(addr, amount) {
let index = allLower.findIndex(item => item.addr === addr && item.amount === amount);
const hexProof = merkleTree.getHexProof(leafNodes[index])
console.log('hexProof: ', hexProof, typeof hexProof)
return hexProof
}
and my solidity verification function takes 2 inputs which are amount(uint256) and merkleProof (byte32). I kept getting invalid merkleProof error.
I am guessing it should be the way I am using js keccak256 is wrong as it should only take one paramemters for input but I am not sure. Can some one helps please
Here is solidity merkleproof verification code
function claimTokens(uint256 amount, bytes32[] calldata merkleProof) public {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
bool valid = MerkleProof.verify(merkleProof, merkleRoot, leaf);
require(valid, "valid proof required.");
require(!claimed[msg.sender], "Tokens already claimed.");
claimed[msg.sender] = true;
emit Claim(msg.sender, amount);
_transfer(address(this), msg.sender, amount);
}
Best Answer
There are several issues with your code. For the example, I'll use Web3.js for some utility functions but feel free to use whatever suits you best. I'd personally use the keccak256 implementations from either Web3.js or Ethers, but I'll keep yours here.
First,
20000000000000000000000000
is too big of a value for the JavaScriptNumber
type to be represented safely, so you may end up with nasty errors due to that. Use string representations / Big Numbers whenever possible when dealing with the blockchain. Then, you must make sure that your input matches exactly what will be hashed on the solidity side.uint256
are encoded on 32 bytes.Second, the implementation of
keccak256
that you are using only expects one parameter and doesn't even consider the other ones, leading to the following behavior :So if you want to stick to that implementation, you must provide them as a single parameter.
With this implementation :
You can successfully verify on the solidity side. I used the following contract :
I hope this answers your question, and I'd advise for unifying everything : use the abi coder and the keccak256 implementation of a single library (i.e., Web3 or Ethers) to avoid those issues.