A library for interacting with Merkle Trees powereed by ethers.js and merkletreejs
npm install ethers-merkletree
A smart contract that uses a Merkle Tree for validating an account:
- Says hello to the
recipient
that paysprice
to the contract. - Both values must be pased, and a
proof
that they are the right values. - If proof is correctly verified, the event
Hello(to)
is emited.
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract PermissionedSalutation {
event Hello(string);
using MerkleProof for bytes32[];
bytes32 public merkleRoot;
function setMerkleRoot(bytes32 pMerkleRoot) public {
merkleRoot = pMerkleRoot
}
function sayHello(address to, uint256 price, bytes32[] memory proof) public {
bytes32 leaf = keccak256(abi.encodePacked(to, price));
require(proof.verify(merkleRoot, leaf), "invalid proof");
require(price = msg.value, "invalid price paid");
emit Hello(to);
}
}
Import WMerkleTree (and types if needed)
import { WMerkleTree, LeafSignature } from 'ethers-merkletree';`
Mock of a list. You should be generating this on your own.
const myAllowList: LeafSourceObject[] = [
{ to: '0x68AC5eE798Ac6F6B0A42F9b3abc3C9FD26dbdeA6',
price: ethers.utils.formatEther("1"),
},
{ to: '0xCf43D97Ed9EC1d458cA69551bcd1Bb157314E0d8',
price: price: ethers.utils.formatEther("1")
}
];
Describe the items structure mapping it to the solidity types on the contract's parameters of function sayHello
.
For each of the parameters, define the name and the solicity type.Names MUST be exact to the source items, but there is no constrains for using the same names on the solidity contract, although I highly recomment it for making code easyly readable.
const leafSignature: LeafSignature = [
{ type: 'address', name: 'to' },
{ type: 'uint256', name: 'price' },
];
Create a new instance of WMerkleTree
(Wrapped Merkle Tree suggestions accepted) passing the leafSignature
and myAllowList
const merkleTree = new WMerkleTree(myAllowList, leafSignature);
You can now get the Merkle root of the Tree.
const merkleRoot = merkleTree.getHexRoot();
await permissionedSalutationContract.setMerkleRoot(merkleRoot);
And request to say hello to Second item on the list, with the evidence that the address can receive a Hello
// Generate the proof of item 1 on the array
const merkleProof = merkleTree.getHexProof(1);
const allowedRecipient = myAllowList[1];
await permissionedSalutationContract.sayHello(item.to item.price, merkleProof, {value: item.price});
Type: T extends LeafSourceObject
An array with the items that make the leaves of the tree.
Type: LeafSignature
An array of objects that define the signature of the leaf items.
nane
the name of the property of the item object type of sourceItems
. It must match the item property name. It is not needed to match the name on the smart contract function, although I highly recommend it for clarity.
type
defines the solidity type to which a parameter resolves. (addess
,uint256
, bytes32
,etc...).