Repository: emilianobonassi/whitehacks-kit Branch: main Commit: 73d2e2947164 Files: 10 Total size: 6.7 KB Directory structure: gitextract_ss5wbbnf/ ├── .github/ │ └── workflows/ │ └── test.yml ├── .gitignore ├── .gitmodules ├── README.md ├── foundry.toml ├── remappings.txt ├── script/ │ └── Whitehack.s.sol ├── src/ │ ├── Factory.sol │ └── Whitehack.sol └── test/ └── Factory.t.sol ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/test.yml ================================================ name: test on: workflow_dispatch env: FOUNDRY_PROFILE: ci jobs: check: strategy: fail-fast: true name: Foundry project runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: version: nightly - name: Run Forge build run: | forge --version forge build --sizes id: build - name: Run Forge tests run: | forge test -vvv id: test ================================================ FILE: .gitignore ================================================ # Compiler files cache/ out/ # Ignores development broadcast logs /broadcast # Docs docs/ # Dotenv file .env ================================================ FILE: .gitmodules ================================================ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts ================================================ FILE: README.md ================================================ # Whitehacks Kit A simple template to perform whitehacks safely in a single tx, leveraging Foundry and Flashbots. ## Disclaimer Provided AS-IS as educational content only, disclaim any liability for using it. ## Usage Whitehacks are hard and should be execute by professionals. If you are unsure reach-out the [Seal 911 Bot](https://t.me/seal_911_bot). Reach-out anyway. This repo offers a guide to prepare them. They must be executed in 1 shot and privately, hence one single transaction and the private mempool by Flashbots. You prepare, you test in a fork, you don't change, you execute. ## Setup 0. Fork the repo 1. [Install Foundry](https://book.getfoundry.sh/getting-started/installation) 2. Edit [Whitehack.sol](./src/Whitehack.sol) 3. Adapt [Whitehack.s.sol](./script/Whitehack.s.sol) ## Preparation 1. Unset `$ETH_RPC_URL` ```zsh unset $ETH_RPC_URL ``` 2. Check no RPC port open on your computer, if so kill the processes ```zsh netstat -an | grep LISTEN | grep 8545 ``` ## Test 1. Run Anvil fork with ```zsh anvil --fork-url https://eth.llamarpc.com ``` 2. Impersonate your account `0xYOUR_WALLET_ADDRESS` ```zsh cast rpc \ anvil_impersonateAccount "0xYOUR_WALLET_ADDRESS" \ --rpc-url "http://localhost:8545" ``` 3. Run the script ```zsh forge script \ script/Whitehack.s.sol:WhitehackScript \ --rpc-url "http://localhost:8545" \ --sender "0xYOUR_WALLET_ADDRESS" \ -vvv \ --broadcast ``` ## Run Do not change your script and contract after the test ```zsh forge script \ script/Whitehack.s.sol:WhitehackScript \ --rpc-url "https://rpc.flashbots.net?hint=hash" \ --sender "0xYOUR_WALLET_ADDRESS" \ --interactives 1 \ -vvv \ --broadcast ``` The rpc url is set for [Full Privacy](https://docs.flashbots.net/flashbots-protect/rpc/mev-share#full-privacy) on Flashbots ## Examples - [Curve 30/07/23](https://github.com/emilianobonassi/curve-whitehack-example) ================================================ FILE: foundry.toml ================================================ [profile.default] src = "src" out = "out" libs = ["lib"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config ================================================ FILE: remappings.txt ================================================ @openzeppelin/=lib/openzeppelin-contracts/ ================================================ FILE: script/Whitehack.s.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "forge-std/Script.sol"; import {Factory} from "../src/Factory.sol"; import {Whitehack} from "../src/Whitehack.sol"; contract WhitehackScript is Script { function run() public { vm.startBroadcast(); console.log(msg.sender, "is running the script"); // 0. Prepare the factory to deploy and execute in 1 go the whitehack // Can be removed if the factory is already deployed Factory factory = new Factory(); console.log("factory deployed at:", address(factory)); // 1. Prepare the whitehack parameters bytes memory params = abi.encode( uint256(42) // put here your params ); // Advanced uint256 ethRequired = 0; // you should not need to send eth to the whitehack // 2. Deploy and execute the whitehack factory.deployAndExec{value: ethRequired}( ethRequired, bytes32('whitehack'), type(Whitehack).creationCode, abi.encodeWithSignature("go(bytes)", params) ); console.log("whitehack executed with success"); vm.stopBroadcast(); } } ================================================ FILE: src/Factory.sol ================================================ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; contract Factory { function deployAndExec( uint256 amount, bytes32 salt, bytes calldata initcode, bytes calldata data ) public payable returns (address, bytes memory) { address addr = Create2.deploy(amount, salt, initcode); (bool success, bytes memory ret) = addr.call(data); require(success, "execution failed"); return (addr, ret); } } ================================================ FILE: src/Whitehack.sol ================================================ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; contract Whitehack { constructor() payable {} function go(bytes calldata params) external returns (bytes memory) { // decode and use params // uint256 param1 = abi.decode(params, (uint256)); return params; } fallback() external {} } ================================================ FILE: test/Factory.t.sol ================================================ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {Test, console2} from "forge-std/Test.sol"; import {Factory} from "../src/Factory.sol"; import {Whitehack} from "../src/Whitehack.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; contract FactoryTest is Test { Factory public factory; function setUp() public { factory = new Factory(); } function testDeployAndExecNoEth() public { bytes memory initcode = type(Whitehack).creationCode; bytes memory params = abi.encode(uint256(42)); bytes memory data = abi.encodeWithSignature("go(bytes)", params); bytes32 salt = bytes32('salt'); address expectedAddr = Create2.computeAddress(bytes32('salt'), keccak256(initcode), address(factory)); (address addr, bytes memory ret) = factory.deployAndExec{value: 0}(0, salt, initcode, data); assertEq(addr, expectedAddr); assertEq(abi.decode(ret, (bytes)), params); } function testDeployAndExecEth() public { bytes memory initcode = type(Whitehack).creationCode; bytes memory params = abi.encode(uint256(42)); bytes memory data = abi.encodeWithSignature("go(bytes)", params); bytes32 salt = bytes32('salt'); uint256 ethBalance = 123; address expectedAddr = Create2.computeAddress(bytes32('salt'), keccak256(initcode), address(factory)); (address addr, bytes memory ret) = factory.deployAndExec{value: ethBalance}(ethBalance, salt, initcode, data); assertEq(addr, expectedAddr); assertEq(abi.decode(ret, (bytes)), params); assertEq(addr.balance, ethBalance); } }