DEV Community

rim dinov
rim dinov

Posted on

Auditing Curve Finance Math: How to Build a Stateful Fuzzer from Scratch

Mathematical precision is the heartbeat of DeFi. In protocols like Curve Finance, where the StableSwap invariant is the foundation, a single rounding error or an unexpected integer overflow can lead to millions in losses.

As a security researcher, I wanted to move beyond basic unit testing and dive deep into the mathematical robustness of the Curve StableSwap NG invariant. In this post, I’ll share how I built a custom stateful fuzzer to stress-test the math behind the protocol.

Why Fuzzing?
Unit tests are great for verifying "happy paths," but they rarely catch the "Edge Cases"—the extreme values where algorithms might behave unexpectedly. Can the contract handle MAX_UINT256? Does it return a zero result for tiny swap amounts? These are the questions that keep auditors awake at night.

The Approach: A Self-Contained PoC
To avoid the overhead of deploying a full pool architecture during every test iteration, I built a self-contained test wrapper in Vyper: TestCurveMath.vy.

This approach isolates the mathematical core, allowing us to:

Isolate the get_y function.

Rapidly execute thousands of iterations.

Inject arbitrary input values.

Фрагмент кода

Simplified logic for demonstration

@external
@view
def test_get_dy(i: int128, j: int128, dx: uint256) -> uint256:
# Logic implementation...
return y
Building the Fuzzer
I used the Ape Framework to orchestrate the tests. The fuzzer script is designed to alternate between "safe" ranges and "danger zones" (Edge Cases).

Python

A snippet from my stateful fuzzer

edge_cases = [0, 1, 10*18, 1024, 2*256 - 1]

for i in range(2000):
if random.random() < 0.2:
x = random.choice(edge_cases)
else:
x = random.randint(10*17, 10*21)

result = tester.test_get_dy(0, 1, x)
assert result >= 0, f"Critical failure at x={x}"
Enter fullscreen mode Exit fullscreen mode

Key Findings & Takeaways
My research focused on three areas:

Precision Loss: By testing dx=1, I verified that the output does not collapse to zero (preventing "dust" attacks or liquidity draining).

Integer Overflow: By pushing 2256 - 1, I ensured the contract either reverts gracefully or handles the math within EVM bounds.

Mathematical Stability: The formula demonstrated robustness across 2,000+ iterations.

Conclusion
Fuzzing isn’t just a "nice to have"—it’s a fundamental part of the security lifecycle for any DeFi protocol. By isolating the math and testing at the boundaries, we can uncover hidden vulnerabilities before they ever hit mainnet.

You can check out the full research, the fuzzing suite, and the audit summary in my GitHub repository:

👉 https://github.com/rdin777/curve-math-fuzzing

security, #defi, #vyper, #smartcontracts

Top comments (0)