-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakeMerkle.s.sol
More file actions
132 lines (113 loc) · 5.56 KB
/
Copy pathMakeMerkle.s.sol
File metadata and controls
132 lines (113 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script} from "forge-std/Script.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {console} from "forge-std/console.sol";
import {Merkle} from "murky/src/Merkle.sol";
import {ScriptHelper} from "murky/script/common/ScriptHelper.sol";
// Merkle proof generator script
// To use:
// 1. Run `forge script script/GenerateInput.s.sol` to generate the input file
// 2. Run `forge script script/Merkle.s.sol`
// 3. The output file will be generated in /script/target/output.json
/**
* @title MakeMerkle
* @author Ciara Nightingale
* @author Cyfrin
*
* Original Work by:fu
* @author kootsZhin
* @notice https://github.com/dmfxyz/murky
*/
contract MakeMerkle is Script, ScriptHelper {
using stdJson for string; // enables us to use the json cheatcodes for strings
Merkle private m = new Merkle(); // instance of the merkle contract from Murky to do shit
string private inputPath = "/script/target/input.json";
string private outputPath = "/script/target/output.json";
string private elements = vm.readFile(string.concat(vm.projectRoot(), inputPath)); // get the absolute path
string[] private types = elements.readStringArray(".types"); // gets the merkle tree leaf types from json using forge standard lib cheatcode
uint256 private count = elements.readUint(".count"); // get the number of leaf nodes
// make three arrays the same size as the number of leaf nodes
bytes32[] private leafs = new bytes32[](count);
string[] private inputs = new string[](count);
string[] private outputs = new string[](count);
string private output;
/// @dev Returns the JSON path of the input file
// output file output ".values.some-address.some-amount"
function getValuesByIndex(uint256 i, uint256 j) internal pure returns (string memory) {
return string.concat(".values.", vm.toString(i), ".", vm.toString(j));
}
/// @dev Generate the JSON entries for the output file
function generateJsonEntries(string memory _inputs, string memory _proof, string memory _root, string memory _leaf)
internal
pure
returns (string memory)
{
string memory result = string.concat(
"{",
"\"inputs\":",
_inputs,
",",
"\"proof\":",
_proof,
",",
"\"root\":\"",
_root,
"\",",
"\"leaf\":\"",
_leaf,
"\"",
"}"
);
return result;
}
/// @dev Read the input file and generate the Merkle proof, then write the output file
function run() public {
console.log("Generating Merkle Proof for %s", inputPath);
for (uint256 i = 0; i < count; ++i) {
string[] memory input = new string[](types.length); // stringified data (address and string both as strings)
bytes32[] memory data = new bytes32[](types.length); // actual data as a bytes32
for (uint256 j = 0; j < types.length; ++j) {
if (compareStrings(types[j], "address")) {
address value = elements.readAddress(getValuesByIndex(i, j));
// you can't immediately cast straight to 32 bytes as an address is 20 bytes so first cast to uint160 (20 bytes) cast up to uint256 which is 32 bytes and finally to bytes32
data[j] = bytes32(uint256(uint160(value)));
input[j] = vm.toString(value);
} else if (compareStrings(types[j], "uint")) {
uint256 value = vm.parseUint(elements.readString(getValuesByIndex(i, j)));
data[j] = bytes32(value);
input[j] = vm.toString(value);
}
}
// Create the hash for the merkle tree leaf node
// abi encode the data array (each element is a bytes32 representation for the address and the amount)
// Helper from Murky (ltrim64) Returns the bytes with the first 64 bytes removed
// ltrim64 removes the offset and length from the encoded bytes. There is an offset because the array
// is declared in memory
// hash the encoded address and amount
// bytes.concat turns from bytes32 to bytes
// hash again because preimage attack
leafs[i] = keccak256(bytes.concat(keccak256(ltrim64(abi.encode(data)))));
// Converts a string array into a JSON array string.
// store the corresponding values/inputs for each leaf node
inputs[i] = stringArrayToString(input);
}
for (uint256 i = 0; i < count; ++i) {
// get proof gets the nodes needed for the proof & stringify (from helper lib)
string memory proof = bytes32ArrayToString(m.getProof(leafs, i));
// get the root hash and stringify
string memory root = vm.toString(m.getRoot(leafs));
// get the specific leaf working on
string memory leaf = vm.toString(leafs[i]);
// get the singified input (address, amount)
string memory input = inputs[i];
// generate the Json output file (tree dump)
outputs[i] = generateJsonEntries(input, proof, root, leaf);
}
// stringify the array of strings to a single string
output = stringArrayToArrayString(outputs);
// write to the output file the stringified output json (tree dump)
vm.writeFile(string.concat(vm.projectRoot(), outputPath), output);
console.log("DONE: The output is found at %s", outputPath);
}
}