A CLI tool that converts Echidna fuzzer reproducer files to executable Foundry test files.
Echidna generates reproducer files in JSON format when it finds bugs or property violations. This tool parses those reproducer files and generates corresponding Foundry test files that can be executed with forge test
to reproduce the exact same sequence of function calls.
- JSON Parsing: Parses Echidna reproducer files with complex ABI parameter encoding
- Type Conversion: Converts ABI types (AbiUInt, AbiInt, AbiBool, etc.) to proper Solidity types
- Template Generation: Uses Go templates to generate clean, readable Foundry test files
- CLI Interface: Simple command-line interface with sensible defaults
- Directory Support: Automatically finds and uses the oldest .txt file when given a directory
- Actor Management: Generates
_setUpActor()
calls for different users - Time Delays: Includes
_delay()
calls for time-based testing - Configurable Output: Customize contract names, test function names, and output paths
- Go 1.21 or higher
go install github.com/Enigma-Dark/runes@latest
git clone https://github.com/Enigma-Dark/runes.git
cd runes
go mod tidy
make build
The binary will be available at build/runes
.
Convert an Echidna reproducer file to a Foundry test:
./runes convert reproducer.txt
Convert all reproducer files in a directory (automatically selects oldest):
./runes convert /path/to/reproducers/
Customize the output:
./runes convert reproducer.txt \
--output MyTest.t.sol \
--contract MyTestContract \
--test testBugReproduction
--output, -o
: Output file path (default:[input-name]_replay.t.sol
)--contract, -c
: Contract name (default:[input-name]Replay
)--test, -t
: Test function name (default:testReplay
)--config
: Config file (default:$HOME/.runes.yaml
)
The tool accepts:
- Single file: A specific .txt reproducer file
- Directory: A folder containing .txt files (automatically selects the oldest)
Echidna reproducer files are in JSON format and contain an array of transaction objects:
[
{
"call": {
"contents": [
"deposit",
[
{"contents": [256, "3625"], "tag": "AbiUInt"},
{"contents": [8, "0"], "tag": "AbiUInt"}
]
],
"tag": "SolCall"
},
"dst": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496",
"delay": ["0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"],
"gas": 1000000000,
"value": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
]
The tool generates clean, readable Foundry test files in the style of modern property-based testing:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Test} from "forge-std/Test.sol";
contract TestReplay is Test {
// Actor addresses (adjust these to match your test setup)
address constant USER1 = 0x0000000000000000000000000000000000010000;
address constant USER2 = 0x0000000000000000000000000000000000020000;
address constant USER3 = 0x0000000000000000000000000000000000030000;
// TODO: Replace with your actual contract instance
// YourContract Tester;
function setUp() public {
// TODO: Initialize your contract here
// Tester = new YourContract();
}
function test_replay() public {
_setUpActor(USER1);
_delay(2);
Tester.deposit(3625, 0, 1);
_setUpActor(USER3);
_delay(840);
Tester.donateEulerOnlyEVaultToTargetEVault(109, 1, 11);
Tester.setPrice(1, 0);
Tester.borrowCV(115792089237316195423570985008687907853269984665640564039457584007913129639935, 0);
}
function _setUpActor(address actor) internal {
vm.startPrank(actor);
// Add any additional actor setup here if needed
}
function _delay(uint256 timeInSeconds) internal {
vm.warp(block.timestamp + timeInSeconds);
}
}
AbiUInt
- Unsigned integers (uint8, uint16, uint256, etc.)AbiInt
- Signed integers (int8, int16, int256, etc.)AbiAddress
- Ethereum addressesAbiBool
- Boolean values (supports both array and direct boolean formats)AbiBytes
- Fixed and dynamic byte arraysAbiString
- String values
./runes convert reproducer.txt
Output: reproducer_replay.t.sol
./runes convert /path/to/reproducers/
Automatically finds the oldest .txt file and converts it.
./runes convert reproducer.txt \
--output BugReproduction.t.sol \
--contract BugReproduction \
--test test_reproduce_bug
When you provide a directory path:
- Scans for .txt files: Finds all .txt files in the directory
- Selects oldest: Automatically selects the file with the oldest modification time
- Processes: Converts the selected file to a Foundry test
This is particularly useful when working with Echidna corpus directories that contain multiple reproducer files.
runes/
├── cmd/ # CLI commands (cobra)
│ ├── root.go # Root command setup
│ └── convert.go # Convert command implementation
├── internal/
│ ├── types/ # Type definitions
│ ├── parser/ # JSON parsing logic
│ └── generator/ # Test file generation
├── main.go # Entry point
├── go.mod # Go module definition
└── README.md # This file
go test ./...
See CONTRIBUTING.md for detailed guidelines.
make test # Run all tests
make test-verbose # Run tests with verbose output
- Add example reproducer files in
/examples
- Support for more ABI types (AbiArray, AbiTuple)
- Better error messages with line numbers
- Support for multi-file test generation
- Integration with more fuzzing tools, like Medusa
- Complex nested ABI types may need manual adjustment
- Generated tests require manual contract initialization
MIT License - see LICENSE file for details.