Skip to content

Commit 28d333f

Browse files
rappieljz3
andauthored
Update readme (#71)
* Initial README.md by Claude * Remove alpha & multi platform * Remove badges * Manual changes * Remove API * Remove String Utilities * Remove * Reorder * Split assertions * Tweaks * Rewrite * Add disclaimer * Minor tweaks * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Rename licence file * Change license to GPL v3 * Undo license file rename * Update license to GPL v3 everywhere * License attributions * Restructure * Contributing * Contributing * Simplified introduction text * Tweaks * Reorder * Update README.md --------- Co-authored-by: ljz3 <[email protected]>
1 parent 66065a5 commit 28d333f

39 files changed

+964
-65
lines changed

CONTRIBUTING.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Contributing to Fuzzlib
2+
3+
Thanks for your interest in contributing to Fuzzlib!
4+
5+
## Development Setup
6+
7+
### Prerequisites
8+
9+
- [Foundry](https://getfoundry.sh/)
10+
11+
### Setup
12+
13+
```bash
14+
git clone https://github.com/your-username/fuzzlib.git
15+
cd fuzzlib
16+
forge install
17+
```
18+
19+
### Testing
20+
21+
```bash
22+
# Run all tests
23+
forge test
24+
25+
# Run tests with increased fuzz runs
26+
forge test --fuzz-runs 10000
27+
28+
# Run Echidna E2E tests
29+
python3 test/e2e/echidna/run-echidna.py
30+
```
31+
32+
## Contributing Workflow
33+
34+
1. Fork the repository
35+
2. Create a feature branch: `git checkout -b feature-name`
36+
3. Make your changes
37+
4. Add tests
38+
5. Ensure all tests pass: `forge test`
39+
6. Format code: `forge fmt`
40+
7. Commit and push
41+
8. Open a Pull Request against the main repository
42+
43+
## Development Guidelines
44+
45+
- Keep code simple and maintainable
46+
- Follow existing code patterns and conventions
47+
- Add comprehensive tests for new functionality
48+
- Update documentation as needed
49+
50+
## Issues
51+
52+
- Check existing issues before creating new ones
53+
- Provide clear reproduction steps
54+
- Include sample code if relevant

LICENSE

Lines changed: 674 additions & 21 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 198 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,205 @@
11
# Fuzzlib
2-
Fuzzlib is an unopinionated Solidity library designed for both stateful and stateless fuzzing of smart contracts. It provides a collection of useful functions and libraries designed to streamline fuzzing harness development with Echidna, Medusa, and Foundry, making the process easier and more efficient.
32

4-
**Fuzzlib is still in alpha and under active development with potential future breaking changes!**
3+
General purpose unopinionated Solidity fuzzing library for stateful and stateless fuzzing. Compatible with Echidna, Medusa, and Foundry.
54

6-
## Branches
7-
* `main`: Latest `fl.` namespace
8-
* `v0_2`: Old namespace version for backwards compatibility
5+
Provides common utilities for fuzz testing through a simple `fl` namespace: assertions, value clamping, logging, math operations, and more.
96

7+
## Key Features
108

11-
## Known limitations
12-
- Signed integer clamping is limited to int128 range to avoid overflow issues in range calculations
9+
- **Basic Assertions**: Helpers for common test conditions and equality checks
10+
- **Advanced Assertions**: Utilities like error handling via `errAllow` for expected failures
11+
- **Value Clamping**: Clamp values to ranges with uniform distribution for better fuzzing
12+
- **Logging Utilities**: Unified logging for debugging and tracing
13+
- **Math Utilities**: Operations like min, max, absolute value, and difference calculations
14+
- **Random Utilities**: Fisher-Yates array shuffling
15+
- **Function Call Helpers**: Utilities for making function calls with actor pranking
16+
- **Comprehensive Testing**: Extensive test suite with both unit and fuzz tests
17+
- **Well-Documented**: Clear and complete, following OpenZeppelin-style conventions
1318

19+
## Installation
1420

21+
### Using Foundry
22+
23+
```bash
24+
forge install perimetersec/fuzzlib
25+
```
26+
27+
### Using npm
28+
29+
```bash
30+
npm install @perimetersec/fuzzlib
31+
```
32+
33+
Add to your `remappings.txt`:
34+
```
35+
fuzzlib/=lib/fuzzlib/src/
36+
```
37+
38+
## Quick Start
39+
40+
Create a simple fuzzing test by extending `FuzzBase`:
41+
42+
```solidity
43+
import {FuzzBase} from "fuzzlib/FuzzBase.sol";
44+
45+
contract MyFuzzer is FuzzBase {
46+
function testMath(uint256 a, uint256 b) public {
47+
// Clamp inputs to reasonable ranges
48+
uint256 x = fl.clamp(a, 0, 1000);
49+
uint256 y = fl.clamp(b, 0, 1000);
50+
51+
// Log for debugging
52+
fl.log("Testing max function. x =", x);
53+
fl.log("Testing max function. y =", y);
54+
55+
// Test mathematical properties
56+
fl.gte(fl.max(x, y), x, "Max should be >= x");
57+
fl.gte(fl.max(x, y), y, "Max should be >= y");
58+
}
59+
}
60+
```
61+
62+
63+
## Function Reference
64+
65+
### Basic Assertions
66+
67+
```solidity
68+
// Fundamental assertions
69+
fl.t(exists, "Property X exists");
70+
fl.eq(result, 100, "Result should equal 100");
71+
fl.neq(userA, userB, "Users should be different");
72+
73+
// Comparison assertions
74+
fl.gt(balance, 1000, "Balance should be greater than 1000");
75+
fl.gte(amount, 50, "Amount should be greater than or equal to 50");
76+
fl.lt(fee, 100, "Fee should be less than 100");
77+
fl.lte(price, 500, "Price should be less than or equal to 500");
78+
```
79+
80+
### Advanced Assertions
81+
82+
```solidity
83+
// Allow specific require messages
84+
string[] memory allowedMessages = new string[](1);
85+
allowedMessages[0] = "Insufficient balance";
86+
fl.errAllow(errorData, allowedMessages, "Message X should be allowed");
87+
88+
// Allow specific custom errors
89+
bytes4[] memory allowedErrors = new bytes4[](1);
90+
allowedErrors[0] = CustomError.selector;
91+
fl.errAllow(errorSelector, allowedErrors, "Error X should be allowed");
92+
93+
// Combined error handling
94+
fl.errAllow(errorData, allowedMessages, allowedErrors, "Either should be allowed");
95+
```
96+
97+
### Value Clamping
98+
99+
```solidity
100+
// Value clamping with uniform distribution
101+
uint256 clamped = fl.clamp(inputValue, 0, 100);
102+
103+
// Clamp to greater than value
104+
uint256 clampedGt = fl.clampGt(inputValue, 50);
105+
106+
// Clamp to greater than or equal
107+
uint256 clampedGte = fl.clampGte(inputValue, 50);
108+
109+
// Clamp to less than value
110+
uint256 clampedLt = fl.clampLt(inputValue, 100);
111+
112+
// Clamp to less than or equal
113+
uint256 clampedLte = fl.clampLte(inputValue, 100);
114+
```
115+
116+
### Logging
117+
118+
```solidity
119+
// Simple logging
120+
fl.log("Testing scenario");
121+
122+
// Logging with values
123+
fl.log("Balance:", balance);
124+
fl.log("User count:", 42);
125+
126+
// Failure logging
127+
fl.logFail("This test failed");
128+
fl.logFail("Invalid amount:", amount);
129+
```
130+
131+
### Math Utilities
132+
133+
```solidity
134+
// Min/max operations
135+
uint256 maximum = fl.max(150, 300);
136+
int256 minimum = fl.min(-50, 25);
137+
138+
// Absolute value and difference
139+
uint256 absolute = fl.abs(-42);
140+
uint256 difference = fl.diff(100, 75);
141+
```
142+
143+
### Random Utilities
144+
145+
```solidity
146+
// Shuffle arrays
147+
uint256[] memory array = new uint256[](10);
148+
fl.shuffleArray(array, entropy);
149+
```
150+
151+
### Function Call Helpers
152+
153+
```solidity
154+
// Make function calls
155+
bytes memory result = fl.doFunctionCall(
156+
address(target),
157+
abi.encodeWithSignature("getValue()"),
158+
msg.sender // actor
159+
);
160+
161+
// Calls with automatic pranking
162+
(bool success, bytes memory data) = fl.doFunctionCall(
163+
address(target),
164+
abi.encodeWithSignature("transfer(address,uint256)", recipient, amount),
165+
sender
166+
);
167+
168+
// Static calls (view functions)
169+
(bool success, bytes memory data) = fl.doFunctionStaticCall(
170+
address(target),
171+
abi.encodeWithSignature("balanceOf(address)", user)
172+
);
173+
```
174+
175+
## Known Limitations
176+
177+
- **Signed Integer Clamping**: Limited to `int128` range to avoid overflow issues in range calculations
178+
- **Gas Optimization**: Library prioritizes functionality over gas optimization
179+
- **Function Selector Clashing**: If the error selector clashes with `Error(string)` when using errAllow, unexpected behavior may happen
180+
181+
182+
## Roadmap
183+
184+
- [ ] Support for more platforms
185+
- [ ] Add more helper functions
186+
- [ ] Performance optimizations
187+
188+
189+
## Contributing
190+
191+
We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.
192+
193+
194+
## License
195+
196+
This project is licensed under the GNU General Public License v3.0. See the [LICENSE](LICENSE) file for details.
197+
198+
Some portions of this code are modified from [Crytic Properties](https://github.com/crytic/properties/blob/main/contracts/util/PropertiesHelper.sol), which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
199+
200+
Some portions of this code are modified from [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol), which is licensed under the MIT License.
201+
202+
203+
## Disclaimer
204+
205+
This software is provided as-is without warranty. The main branch contains new and experimental features that may be unstable. For production use, we recommend using official tagged releases which have been thoroughly tested. While we are not responsible for any bugs or issues, we maintain a bug bounty program that applies to official releases only.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Solidity Fuzzing Library",
55
"homepage": "https://github.com/perimetersec/fuzzlib#readme",
66
"author": "Perimeter <[email protected]>",
7-
"license": "MIT",
7+
"license": "GPL-3.0",
88
"files": [
99
"src/**/*"
1010
],

src/Constants.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
/**

src/FuzzBase.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
import {Fuzzlib} from "./Fuzzlib.sol";

src/FuzzLibString.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
/**

src/FuzzSafeCast.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
/**

src/Fuzzlib.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
import {HelperBase} from "./helpers/HelperBase.sol";

src/IHevm.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: MIT
1+
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

44
import {Constants} from "./Constants.sol";

0 commit comments

Comments
 (0)