-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7c8e48d
commit 324d987
Showing
9 changed files
with
273 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,9 @@ | ||
## Foundry | ||
1. GenerateInput: Create the input JSON file to create Merkle Tree with, | ||
|
||
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
- the eligible addresses | ||
- corresponding amount they can receive | ||
|
||
Foundry consists of: | ||
2. MakeMerkle: This will Create Merkle Tree using the input file generated by generateInput. | ||
|
||
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). | ||
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. | ||
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. | ||
- **Chisel**: Fast, utilitarian, and verbose solidity REPL. | ||
|
||
## Documentation | ||
|
||
https://book.getfoundry.sh/ | ||
|
||
## Usage | ||
|
||
### Build | ||
|
||
```shell | ||
$ forge build | ||
``` | ||
|
||
### Test | ||
|
||
```shell | ||
$ forge test | ||
``` | ||
|
||
### Format | ||
|
||
```shell | ||
$ forge fmt | ||
``` | ||
|
||
### Gas Snapshots | ||
|
||
```shell | ||
$ forge snapshot | ||
``` | ||
|
||
### Anvil | ||
|
||
```shell | ||
$ anvil | ||
``` | ||
|
||
### Deploy | ||
|
||
```shell | ||
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key> | ||
``` | ||
|
||
### Cast | ||
|
||
```shell | ||
$ cast <subcommand> | ||
``` | ||
|
||
### Help | ||
|
||
```shell | ||
$ forge --help | ||
$ anvil --help | ||
$ cast --help | ||
``` | ||
- create the hashes, the intermediate hashes and the root hash, | ||
- create the output files that contains all of leaf hashes, the proofs for each leaf node and the root hash. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// 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"; | ||
|
||
// Merkle tree input file generator script | ||
contract GenerateInput is Script { | ||
uint256 private constant AMOUNT = 25 * 1e18; | ||
string[] types = new string[](2); | ||
uint256 count; | ||
string[] whitelist = new string[](4); | ||
string private constant INPUT_PATH = "/script/target/input.json"; | ||
|
||
function run() public { | ||
types[0] = "address"; | ||
types[1] = "uint"; | ||
whitelist[0] = "0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D"; | ||
whitelist[1] = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; | ||
whitelist[2] = "0x2ea3970Ed82D5b30be821FAAD4a731D35964F7dd"; | ||
whitelist[3] = "0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D"; | ||
count = whitelist.length; | ||
string memory input = _createJSON(); | ||
// write to the output file the stringified output json tree dumpus | ||
vm.writeFile(string.concat(vm.projectRoot(), INPUT_PATH), input); | ||
|
||
console.log("DONE: The output is found at %s", INPUT_PATH); | ||
} | ||
|
||
function _createJSON() internal view returns (string memory) { | ||
string memory countString = vm.toString(count); // convert count to string | ||
string memory amountString = vm.toString(AMOUNT); // convert amount to string | ||
string memory json = string.concat( | ||
'{ "types": ["address", "uint"], "count":', | ||
countString, | ||
',"values": {' | ||
); | ||
for (uint256 i = 0; i < whitelist.length; i++) { | ||
if (i == whitelist.length - 1) { | ||
json = string.concat( | ||
json, | ||
'"', | ||
vm.toString(i), | ||
'"', | ||
': { "0":', | ||
'"', | ||
whitelist[i], | ||
'"', | ||
', "1":', | ||
'"', | ||
amountString, | ||
'"', | ||
" }" | ||
); | ||
} else { | ||
json = string.concat( | ||
json, | ||
'"', | ||
vm.toString(i), | ||
'"', | ||
': { "0":', | ||
'"', | ||
whitelist[i], | ||
'"', | ||
', "1":', | ||
'"', | ||
amountString, | ||
'"', | ||
" }," | ||
); | ||
} | ||
} | ||
json = string.concat(json, "} }"); | ||
|
||
return json; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// 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: | ||
* @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 & strigify (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 dumpus | ||
vm.writeFile(string.concat(vm.projectRoot(), outputPath), output); | ||
|
||
console.log("DONE: The output is found at %s", outputPath); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"types": [ | ||
"address", | ||
"uint" | ||
], | ||
"count": 4, | ||
"values": { | ||
"0": { | ||
"0": "0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D", | ||
"1": "25000000000000000000" | ||
}, | ||
"1": { | ||
"0": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", | ||
"1": "25000000000000000000" | ||
}, | ||
"2": { | ||
"0": "0x2ea3970Ed82D5b30be821FAAD4a731D35964F7dd", | ||
"1": "25000000000000000000" | ||
}, | ||
"3": { | ||
"0": "0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D", | ||
"1": "25000000000000000000" | ||
} | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.24; | ||
|
||
import {Test} from "forge-std/Test.sol" | ||
|
||
contract MerkleAirdropTest is Test { | ||
|
||
} |