UNPKG

@dydxfoundation/governance

Version:
98 lines (97 loc) 3.32 kB
"use strict"; // Based on https://github.com/Uniswap/merkle-distributor/blob/c3255bfa2b684594ecd562cacd7664b0f18330bf/src/merkle-tree.ts Object.defineProperty(exports, "__esModule", { value: true }); const ethereumjs_util_1 = require("ethereumjs-util"); class MerkleTree { constructor(elements) { this.elements = [...elements]; // Sort elements this.elements.sort(Buffer.compare); // Deduplicate elements this.elements = MerkleTree.bufDedup(this.elements); this.bufferElementPositionIndex = this.elements.reduce((memo, el, index) => { memo[(0, ethereumjs_util_1.bufferToHex)(el)] = index; return memo; }, {}); // Create layers this.layers = this._getLayers(this.elements); } _getLayers(elements) { if (elements.length === 0) { throw new Error('empty tree'); } const layers = []; layers.push(elements); // Get next layer until we reach the root while (layers[layers.length - 1].length > 1) { layers.push(this._getNextLayer(layers[layers.length - 1])); } return layers; } _getNextLayer(elements) { return elements.reduce((layer, el, idx, arr) => { if (idx % 2 === 0) { // Hash the current element with its pair element. layer.push(MerkleTree.combinedHash(el, arr[idx + 1])); } return layer; }, []); } static combinedHash(first, second) { if (!first) { return second; } if (!second) { return first; } return (0, ethereumjs_util_1.keccak256)(MerkleTree.sortAndConcat(first, second)); } getRoot() { return this.layers[this.layers.length - 1][0]; } getHexRoot() { return (0, ethereumjs_util_1.bufferToHex)(this.getRoot()); } _getProof(el) { let idx = this.bufferElementPositionIndex[(0, ethereumjs_util_1.bufferToHex)(el)]; if (typeof idx !== 'number') { throw new Error('Element does not exist in Merkle tree'); } return this.layers.reduce((proof, layer) => { const pairElement = MerkleTree.getPairElement(idx, layer); if (pairElement) { proof.push(pairElement); } idx = Math.floor(idx / 2); return proof; }, []); } _getHexProof(el) { const proof = this._getProof(el); return MerkleTree.bufArrToHexArr(proof); } static getPairElement(idx, layer) { const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1; if (pairIdx < layer.length) { return layer[pairIdx]; } else { return null; } } static bufDedup(elements) { return elements.filter((el, idx) => { return idx === 0 || !elements[idx - 1].equals(el); }); } static bufArrToHexArr(arr) { if (arr.some((el) => !Buffer.isBuffer(el))) { throw new Error('Array is not an array of buffers'); } return arr.map((el) => '0x' + el.toString('hex')); } static sortAndConcat(...args) { return Buffer.concat([...args].sort(Buffer.compare)); } } exports.default = MerkleTree;