Intro
Product update. (The devs listened.)
tl;dr: assertions are way more granular.
We just shipped callstack forking - and it's exactly what devs needed. And we also shipped a big improvement to the devex. Read on for the details.
What Changed
Before: You could only compare the pre- and post-states of a transaction. Additionally, it was possible to obtain all call inputs of all function calls within a transaction or all changes to a storage slot, which allowed for the reconstruction of data. However, it wasn’t possible to check a specific value of an external contract inside the call stack. For example, if a protocol uses an oracle.getPrice() function, but doesn’t store the value anywhere, it wasn’t possible to check the value.
Now: You can step through a transaction call by call, inspecting state changes at every point in the execution. If the price reported by `oracle.getPrice()` deviates by more than allowed at any point in the call stack, it will be detected and prevented.
This is exactly how price manipulation attacks work (and they get more sophisticated every day). An attacker will:
- Start a transaction (state looks normal)
- Flash loan and manipulate prices mid-transaction
- Exploit your protocol with the manipulated prices
- Fix the prices before the transaction ends
- End transaction (state looks normal again)
Even formal verification can't help here. It can prove your contract behaves correctly under all possible inputs, but it can't see when those inputs are being manipulated mid-execution by external calls.
The Solution: Callstack Forking
We're introducing callstack forking, which is the ability to "fork" state at any point during transaction execution, not just at the beginning and end.
Here's the new cheatcode:
.png)
You can literally step through your transaction call-by-call, writing assertions to check and see exactly what's happening at each step.
How to Use It
Let's say you're protecting a lending protocol against oracle manipulation. Here's a real assertion:
.png)
This catches any attempt to manipulate the oracle price during a borrow operation, even if the price gets "fixed" later in the same transaction.
What this means:
We analyzed major DeFi hacks to determine how many could have been prevented by callstack forking. The results were pretty striking:
Would have been prevented:
- KiloEx ($1.6M) - Price manipulation during position opening
- Mango Markets ($117M) - Oracle price deviation mid-transaction
- PancakeBunny ($45M) - Reward calculation with manipulated prices
- BonqDAO ($120M) - Manual oracle update exploitation
- Inverse Finance ($15M) - DOLA price deviation during flash loan
- Polter Finance - ChainlinkUniV2Adapter price validation failure
- Vicuna Finance - Price manipulation during borrow operations
Total prevented: 7 out of 9 major price manipulation attacks
The two that wouldn't be caught were multi-block attacks (Venus Protocol, Inverse Finance round 1) that happened over hours, not within single transactions.
Major Protocols That Benefit
Looking at how the biggest DeFi protocols work, most would get immediate value:
Aave v3: Uses getAssetPrice() calls throughout liquidations and borrows. Callstack forking lets you verify prices stay consistent across all these calls within a transaction.
Compound/Comet: Similar pattern - fetches Chainlink prices on-demand. Perfect for detecting mid-transaction price anomalies.
Morpho: External oracle calls during lending operations. You can now verify the oracle isn't being manipulated between position updates.
GMX: Already stores prices, but callstack forking makes verification much cleaner than parsing storage slots.
Uniswap v4: Internal price calculations that could be manipulated. Track sqrtPriceX96 changes call by call.
Beyond Price Manipulation
While price manipulation is the obvious use case, callstack forking unlocks way more:
State Consistency Checks: Verify that critical protocol invariants hold at every step, not just the beginning and end.
Multi-Call Validation: For protocols that involve batch operations, ensure each operation in the batch meets your requirements.
Reentrancy Detection: See exactly when and how external calls modify your state.
And Better Testing Too
We completely rewrote the testing interface based on your feedback. The old way was...not great:
.png)
.png)
State persists after function calls in tests now, you can test specific assertion functions in isolation, and you can match on exact revert reasons for more precise testing.
Try It Out
Reach out to get access to the demo to try it out live.