@dydxfoundation/governance
Version:
dYdX governance smart contracts
98 lines (97 loc) • 3.32 kB
JavaScript
;
// 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;