Repository: pcaversaccio/snekmate (language: python)

Issue:
♻️ Refactor `MerkleProofVerification` Scripts
We should refactor the Merkle proof verification [scripts](https://github.com/pcaversaccio/snekmate/tree/main/test/utils/scripts) using the new and well-maintained library [`@openzeppelin/merkle-tree`](https://github.com/OpenZeppelin/merkle-tree). Also, see the discussion here https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3813.


Discussion / hints:

Fix patch:
diff --git a/package.json b/package.json
index aedff4fd..9f2f8c87 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
     "lint:fix": "yarn prettier:fix && yarn solhint:fix && eslint --ext .js . --fix"
   },
   "devDependencies": {
+    "@openzeppelin/merkle-tree": "^1.0.1",
     "eslint": "^8.28.0",
     "eslint-config-prettier": "^8.5.0",
     "ethers": "^5.7.2",
diff --git a/src/utils/MerkleProofVerification.vy b/src/utils/MerkleProofVerification.vy
index 434b0fe7..58fa443e 100644
--- a/src/utils/MerkleProofVerification.vy
+++ b/src/utils/MerkleProofVerification.vy
@@ -3,15 +3,23 @@
 @title Merkle Tree Proof Verification Functions
 @license GNU Affero General Public License v3.0
 @author pcaversaccio
-@notice The proofs can be generated using the JavaScript library:
-        https://github.com/miguelmota/merkletreejs. You must select
+@notice The Merkle tree and the corresponding proofs can be generated
+        using the following JavaScript libraries:
+        - https://github.com/OpenZeppelin/merkle-tree (recommended),
+        - https://github.com/miguelmota/merkletreejs (deprecated).
+        If you are using the (deprecated) JavaScript library `merkletreej`
+        (https://github.com/miguelmota/merkletreejs), you must select
         `keccak256` as the hashing algorithm and pair sorting should
         be enabled. You should avoid using 64-byte leaf values before
         hashing or using a hash function other than `keccak256` for
         hashing leaves. The reason for this is that the concatenation
         of a sorted pair of internal nodes in the Merkle tree could be
-        reinterpreted as a leaf value. OpenZeppelin provides some good
-        examples of how to construct Merkle tree proofs correctly:
+        reinterpreted as a leaf value. OpenZeppelin's JavaScript library
+        `merkle-tree` (https://github.com/OpenZeppelin/merkle-tree)
+        generates Merkle trees that are safe against this attack out of
+        the box. You will find a quick start guide in the README.
+        OpenZeppelin provides some good examples of how to construct
+        Merkle tree proofs correctly:
         https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/utils/cryptography/MerkleProof.test.js.
         The implementation is inspired by OpenZeppelin's implementation here:
         https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol.
diff --git a/yarn.lock b/yarn.lock
index ecbba876..be14670d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -378,6 +378,21 @@
   resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
   integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
 
+"@noble/hashes@1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
+  integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==
+
+"@noble/hashes@~1.1.1":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.3.tgz#360afc77610e0a61f3417e497dcf36862e4f8111"
+  integrity sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A==
+
+"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0":
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94"
+  integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==
+
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -399,6 +414,36 @@
     "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
+"@openzeppelin/merkle-tree@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@openzeppelin/merkle-tree/-/merkle-tree-1.0.1.tgz#36feaae6192aeb509bfd1abd047d18cd3d2bd8ac"
+  integrity sha512-t3D49zshHMV5gJGmD10CTQ4V41JIwWhLQ0btSLmkZLLZSMjLvZKMDDGcI7p2xuLQMjtrOucgf5usgHQIfQ7jUg==
+  dependencies:
+    "@ethersproject/abi" "^5.7.0"
+    ethereum-cryptography "^1.1.2"
+
+"@scure/base@~1.1.0":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
+  integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
+
+"@scure/bip32@1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b"
+  integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==
+  dependencies:
+    "@noble/hashes" "~1.1.1"
+    "@noble/secp256k1" "~1.6.0"
+    "@scure/base" "~1.1.0"
+
+"@scure/bip39@1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a"
+  integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==
+  dependencies:
+    "@noble/hashes" "~1.1.1"
+    "@scure/base" "~1.1.0"
+
 "@solidity-parser/parser@^0.14.5":
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804"
@@ -843,6 +888,16 @@ ethereum-cryptography@^0.1.3:
     secp256k1 "^4.0.1"
     setimmediate "^1.0.5"
 
+ethereum-cryptography@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d"
+  integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==
+  dependencies:
+    "@noble/hashes" "1.1.2"
+    "@noble/secp256k1" "1.6.3"
+    "@scure/bip32" "1.1.0"
+    "@scure/bip39" "1.1.0"
+
 ethereumjs-util@^7.1.0:
   version "7.1.5"
   resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181"


Test patch:
diff --git a/test/utils/MerkleProofVerification.t.sol b/test/utils/MerkleProofVerification.t.sol
index 6a4591e4..0791406d 100644
--- a/test/utils/MerkleProofVerification.t.sol
+++ b/test/utils/MerkleProofVerification.t.sol
@@ -11,6 +11,98 @@ contract MerkleProofVerificationTest is Test {
 
     IMerkleProofVerification private merkleProofVerification;
 
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * proof after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible 32-byte array type.
+     * @return bytes32[] The 32-byte array containing sibling hashes
+     * on the branch from the `leaf` to the `root` of the Merkle tree.
+     */
+    function decodeCorrectProofPayload() internal returns (bytes32[] memory) {
+        bytes32[] memory proofDecoded = new bytes32[](6);
+        string[] memory cmdsProof = new string[](2);
+        cmdsProof[0] = "node";
+        cmdsProof[1] = "test/utils/scripts/generate-proof.js";
+        bytes memory proof = vm.ffi(cmdsProof);
+        (
+            bytes32 arg1,
+            bytes32 arg2,
+            bytes32 arg3,
+            bytes32 arg4,
+            bytes32 arg5,
+            bytes32 arg6
+        ) = abi.decode(
+                proof,
+                (bytes32, bytes32, bytes32, bytes32, bytes32, bytes32)
+            );
+        proofDecoded[0] = arg1;
+        proofDecoded[1] = arg2;
+        proofDecoded[2] = arg3;
+        proofDecoded[3] = arg4;
+        proofDecoded[4] = arg5;
+        proofDecoded[5] = arg6;
+        return proofDecoded;
+    }
+
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * proof after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible 32-byte array type.
+     * @notice This `internal` helper function is used to demonstrate that it
+     * is also possible to create valid proofs for certain 64-byte values that
+     * are *not* contained in `elements` (https://github.com/pcaversaccio/snekmate/blob/main/test/utils/scripts/elements.js).
+     * @return bytes32[] The 32-byte array containing sibling hashes
+     * on the branch from the `leaf` to the `root` of the Merkle tree.
+     */
+    function decodeNoSuchLeafProofPayload()
+        internal
+        returns (bytes32[] memory)
+    {
+        bytes32[] memory proofDecodedSliced = new bytes32[](6);
+        string[] memory cmdsProof = new string[](2);
+        cmdsProof[0] = "node";
+        cmdsProof[1] = "test/utils/scripts/generate-proof-no-such-leaf.js";
+        bytes memory proof = vm.ffi(cmdsProof);
+        (
+            ,
+            bytes32 arg2,
+            bytes32 arg3,
+            bytes32 arg4,
+            bytes32 arg5,
+            bytes32 arg6,
+            bytes32 arg7
+        ) = abi.decode(
+                proof,
+                (bytes32, bytes32, bytes32, bytes32, bytes32, bytes32, bytes32)
+            );
+        proofDecodedSliced[0] = arg2;
+        proofDecodedSliced[1] = arg3;
+        proofDecodedSliced[2] = arg4;
+        proofDecodedSliced[3] = arg5;
+        proofDecodedSliced[4] = arg6;
+        proofDecodedSliced[5] = arg7;
+        return proofDecodedSliced;
+    }
+
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * proof after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible 32-byte array type.
+     * @notice This `internal` helper function is used to decode a bad proof.
+     * @return bytes32[] The 32-byte array containing sibling hashes
+     * on the branch from the `leaf` to the `root` of the Merkle tree.
+     */
+    function decodeBadProofPayload() internal returns (bytes32[] memory) {
+        bytes32[] memory proofDecoded = new bytes32[](1);
+        string[] memory cmdsProof = new string[](2);
+        cmdsProof[0] = "node";
+        cmdsProof[1] = "test/utils/scripts/generate-bad-proof.js";
+        bytes memory proof = vm.ffi(cmdsProof);
+        bytes32 arg = abi.decode(proof, (bytes32));
+        proofDecoded[0] = arg;
+        return proofDecoded;
+    }
+
     function setUp() public {
         merkleProofVerification = IMerkleProofVerification(
             vyperDeployer.deployContract(
@@ -20,65 +112,6 @@ contract MerkleProofVerificationTest is Test {
         );
     }
 
-    function decode(
-        bool flag
-    ) internal returns (bytes32[] memory, bytes32[] memory) {
-        bytes32[] memory proofDecoded = new bytes32[](7);
-        bytes32[] memory proofDecodedSliced = new bytes32[](6);
-
-        if (flag) {
-            string[] memory cmdsProof = new string[](2);
-            cmdsProof[0] = "node";
-            cmdsProof[1] = "test/utils/scripts/generate-proof.js";
-            bytes memory proof = vm.ffi(cmdsProof);
-            (
-                bytes32 arg1,
-                bytes32 arg2,
-                bytes32 arg3,
-                bytes32 arg4,
-                bytes32 arg5,
-                bytes32 arg6,
-                bytes32 arg7
-            ) = abi.decode(
-                    proof,
-                    (
-                        bytes32,
-                        bytes32,
-                        bytes32,
-                        bytes32,
-                        bytes32,
-                        bytes32,
-                        bytes32
-                    )
-                );
-            proofDecoded[0] = arg1;
-            proofDecoded[1] = arg2;
-            proofDecoded[2] = arg3;
-            proofDecoded[3] = arg4;
-            proofDecoded[4] = arg5;
-            proofDecoded[5] = arg6;
-            proofDecoded[6] = arg7;
-
-            proofDecodedSliced[0] = arg2;
-            proofDecodedSliced[1] = arg3;
-            proofDecodedSliced[2] = arg4;
-            proofDecodedSliced[3] = arg5;
-            proofDecodedSliced[4] = arg6;
-            proofDecodedSliced[5] = arg7;
-
-            return (proofDecoded, proofDecodedSliced);
-        } else {
-            string[] memory cmdsProof = new string[](2);
-            cmdsProof[0] = "node";
-            cmdsProof[1] = "test/utils/scripts/generate-bad-proof.js";
-            bytes memory proof = vm.ffi(cmdsProof);
-            bytes32 arg1 = abi.decode(proof, (bytes32));
-            proofDecoded[0] = arg1;
-
-            return (proofDecoded, proofDecodedSliced);
-        }
-    }
-
     function testVerify() public {
         string[] memory cmdsRoot = new string[](2);
         cmdsRoot[0] = "node";
@@ -90,15 +123,7 @@ contract MerkleProofVerificationTest is Test {
         cmdsLeaf[1] = "test/utils/scripts/generate-leaf.js";
         bytes memory leaf = vm.ffi(cmdsLeaf);
 
-        string[] memory cmdsNoSuchLeaf = new string[](2);
-        cmdsNoSuchLeaf[0] = "node";
-        cmdsNoSuchLeaf[1] = "test/utils/scripts/generate-no-such-leaf.js";
-        bytes memory noSuchLeaf = vm.ffi(cmdsNoSuchLeaf);
-
-        (
-            bytes32[] memory proofDecoded,
-            bytes32[] memory proofDecodedSliced
-        ) = decode(true);
+        bytes32[] memory proofDecoded = decodeCorrectProofPayload();
 
         assertTrue(
             merkleProofVerification.verify(
@@ -107,10 +132,25 @@ contract MerkleProofVerificationTest is Test {
                 bytes32(leaf)
             )
         );
+
+        string[] memory cmdsRootNoSuchLeaf = new string[](2);
+        cmdsRootNoSuchLeaf[0] = "node";
+        cmdsRootNoSuchLeaf[
+            1
+        ] = "test/utils/scripts/generate-root-no-such-leaf.js";
+        bytes memory rootNoSuchLeaf = vm.ffi(cmdsRootNoSuchLeaf);
+
+        string[] memory cmdsNoSuchLeaf = new string[](2);
+        cmdsNoSuchLeaf[0] = "node";
+        cmdsNoSuchLeaf[1] = "test/utils/scripts/generate-no-such-leaf.js";
+        bytes memory noSuchLeaf = vm.ffi(cmdsNoSuchLeaf);
+
+        bytes32[] memory proofDecodedSliced = decodeNoSuchLeafProofPayload();
+
         assertTrue(
             merkleProofVerification.verify(
                 proofDecodedSliced,
-                bytes32(root),
+                bytes32(rootNoSuchLeaf),
                 bytes32(noSuchLeaf)
             )
         );
@@ -127,7 +167,7 @@ contract MerkleProofVerificationTest is Test {
         cmdsCorrectLeaf[1] = "test/utils/scripts/generate-leaf.js";
         bytes memory leaf = vm.ffi(cmdsCorrectLeaf);
 
-        (bytes32[] memory badProofDecoded, ) = decode(false);
+        bytes32[] memory badProofDecoded = decodeBadProofPayload();
 
         assertTrue(
             !merkleProofVerification.verify(
@@ -149,7 +189,7 @@ contract MerkleProofVerificationTest is Test {
         cmdsLeaf[1] = "test/utils/scripts/generate-leaf.js";
         bytes memory leaf = vm.ffi(cmdsLeaf);
 
-        (bytes32[] memory proofDecoded, ) = decode(true);
+        bytes32[] memory proofDecoded = decodeCorrectProofPayload();
 
         bytes32[] memory proofInvalidLengthDecoded = new bytes32[](3);
         proofInvalidLengthDecoded[0] = proofDecoded[0];
diff --git a/test/utils/scripts/generate-bad-proof.js b/test/utils/scripts/generate-bad-proof.js
index 1cba5d73..821e5d7e 100644
--- a/test/utils/scripts/generate-bad-proof.js
+++ b/test/utils/scripts/generate-bad-proof.js
@@ -1,10 +1,13 @@
-const { MerkleTree } = require("merkletreejs");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
 const ethers = require("ethers");
 
 const badElements = ["d", "e", "f"];
-const badMerkleTree = new MerkleTree(badElements);
+const merkleTree = StandardMerkleTree.of(
+  badElements.map((c) => [c]),
+  ["string"]
+);
 
-const badProof = badMerkleTree.getHexProof(badElements[0]);
+const badProof = merkleTree.getProof([badElements[0]]);
 
 // eslint-disable-next-line no-undef
 process.stdout.write(
diff --git a/test/utils/scripts/generate-leaf.js b/test/utils/scripts/generate-leaf.js
index 384e8f46..a0ba61e2 100644
--- a/test/utils/scripts/generate-leaf.js
+++ b/test/utils/scripts/generate-leaf.js
@@ -1,8 +1,12 @@
-const keccak256 = require("keccak256");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
 
 const elements = require("./elements.js");
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
 
-const leaf = "0x" + keccak256(elements[0]).toString("hex");
+const leaf = merkleTree.leafHash([elements[0]]);
 
 // eslint-disable-next-line no-undef
 process.stdout.write(leaf);
diff --git a/test/utils/scripts/generate-no-such-leaf.js b/test/utils/scripts/generate-no-such-leaf.js
index 1569bd42..70a72721 100644
--- a/test/utils/scripts/generate-no-such-leaf.js
+++ b/test/utils/scripts/generate-no-such-leaf.js
@@ -2,7 +2,7 @@ const keccak256 = require("keccak256");
 
 const elements = require("./elements.js");
 
-// For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements
+// For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in `elements`
 const noSuchLeaf =
   "0x" +
   keccak256(
diff --git a/test/utils/scripts/generate-proof-no-such-leaf.js b/test/utils/scripts/generate-proof-no-such-leaf.js
new file mode 100644
index 00000000..8db70e01
--- /dev/null
+++ b/test/utils/scripts/generate-proof-no-such-leaf.js
@@ -0,0 +1,28 @@
+const { MerkleTree } = require("merkletreejs");
+const keccak256 = require("keccak256");
+const ethers = require("ethers");
+
+const elements = require("./elements.js");
+const merkleTree = new MerkleTree(elements, keccak256, {
+  hashLeaves: true,
+  sortPairs: true,
+});
+
+const leaf = "0x" + keccak256(elements[0]).toString("hex");
+const proof = merkleTree.getHexProof(leaf);
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(
+    [
+      "bytes32",
+      "bytes32",
+      "bytes32",
+      "bytes32",
+      "bytes32",
+      "bytes32",
+      "bytes32",
+    ],
+    proof
+  )
+);
diff --git a/test/utils/scripts/generate-proof.js b/test/utils/scripts/generate-proof.js
index 8db70e01..04a08324 100644
--- a/test/utils/scripts/generate-proof.js
+++ b/test/utils/scripts/generate-proof.js
@@ -1,28 +1,18 @@
-const { MerkleTree } = require("merkletreejs");
-const keccak256 = require("keccak256");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
 const ethers = require("ethers");
 
 const elements = require("./elements.js");
-const merkleTree = new MerkleTree(elements, keccak256, {
-  hashLeaves: true,
-  sortPairs: true,
-});
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
 
-const leaf = "0x" + keccak256(elements[0]).toString("hex");
-const proof = merkleTree.getHexProof(leaf);
+const proof = merkleTree.getProof([elements[0]]);
 
 // eslint-disable-next-line no-undef
 process.stdout.write(
   ethers.utils.defaultAbiCoder.encode(
-    [
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-    ],
+    ["bytes32", "bytes32", "bytes32", "bytes32", "bytes32", "bytes32"],
     proof
   )
 );
diff --git a/test/utils/scripts/generate-root-no-such-leaf.js b/test/utils/scripts/generate-root-no-such-leaf.js
new file mode 100644
index 00000000..1bf26bdf
--- /dev/null
+++ b/test/utils/scripts/generate-root-no-such-leaf.js
@@ -0,0 +1,13 @@
+const { MerkleTree } = require("merkletreejs");
+const keccak256 = require("keccak256");
+
+const elements = require("./elements.js");
+const merkleTree = new MerkleTree(elements, keccak256, {
+  hashLeaves: true,
+  sortPairs: true,
+});
+
+const root = merkleTree.getHexRoot();
+
+// eslint-disable-next-line no-undef
+process.stdout.write(root);
diff --git a/test/utils/scripts/generate-root.js b/test/utils/scripts/generate-root.js
index 1bf26bdf..a960d363 100644
--- a/test/utils/scripts/generate-root.js
+++ b/test/utils/scripts/generate-root.js
@@ -1,13 +1,12 @@
-const { MerkleTree } = require("merkletreejs");
-const keccak256 = require("keccak256");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
 
 const elements = require("./elements.js");
-const merkleTree = new MerkleTree(elements, keccak256, {
-  hashLeaves: true,
-  sortPairs: true,
-});
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
 
-const root = merkleTree.getHexRoot();
+const root = merkleTree.root;
 
 // eslint-disable-next-line no-undef
 process.stdout.write(root);