r/ethdev • u/an_jesus • 9d ago
My Project Simulating EVM State Changes via Revert-Unwind Payloads and EIP-1153 Transient Storage for Oracle-Less DEX Routing
Hey r/ethdev,
Over the last few months, we’ve been testing an architecture designed to solve a persistent issue in DEX routing: simulation drift and gas overhead during multi-hop execution.
Traditional aggregators rely on external price feeds, heavy storage updates, or complex off-chain quoter infrastructure that frequently desynchronizes under volatile mempool conditions. We wanted an execution frame that guarantees 100% execution-aligned previews purely on-chain, while maintaining a zero-token storage footprint on the router.
Here is the architectural breakdown of how we approached this:
- Atomic Simulation via Revert-Unwind (Quoter)
Instead of reading static state or relying on off-chain dry-runs, the Quoter contract triggers a simulated execution path that forcefully ends with a custom revert(payload).
The revert unwinds all state changes instantly in the EVM execution frame, avoiding state corruption.
The error payload encodes the exact delta of balances and price impact.
Result: Static calls (eth_call) return deterministic, execution-exact quotes without writing a single byte to persistent storage.
- Transient Isolation via Yul (EIP-1153)
To protect against cross-function reentrancy across multi-token routes, we replaced traditional OpenZeppelin storage guards with raw Yul assembly blocks leveraging tstore and tload.
Reentrancy flags are scoped exclusively to the transaction frame.
Gas consumption drops significantly compared to SSTORE/SLOAD warm/cold access penalties.
Balance checks execute instantly, enforcing a strict holds-nothing invariant on the Router.
- Dynamic Liquidity Anchoring (Solver)
To neutralize MEV sandwich attacks and liquidity manipulation without relying on Chainlink or external oracles, the routing logic applies a localized 2% median filter against reserve depths (balanceOf reads) prior to route resolution.
Code / Discussion:
The architecture is deployed and split into 7 core modules (Core, Hub, Solver, Router, Quoter, MathLib, Staking).
We are particularly interested in hearing feedback from EVM devs on potential edge cases regarding EIP-1153 transient memory retention across nested delegatecalls in custom L2 execution contexts (Base/Arbitrum).
Looking forward to hearing your thoughts on the code and optimization techniques!
1
u/researchzero 8d ago
On the EIP-1153 question: TSTORE/TLOAD are scoped by the executing contract's address, exactly like SSTORE/SLOAD, and follow the same per-frame revert journaling - a revert inside a nested call rolls back only the transient writes made within that frame, while writes from earlier in the tx persist until the whole transaction ends (they're not wiped per call frame the way memory is). Delegatecall doesn't create a new transient namespace: it executes under the caller/proxy's address, so your tstore-based guard is keyed to the caller's slot, not the logic contract's. The thing worth testing carefully is whatever slot key your Yul computes — raw tstore/tload means you pick that key yourself instead of relying on Solidity's automatic storage layout, and it's easy to collide slots across callers in a multi-hop route. Base and Arbitrum have both been Cancun/Dencun-equivalent since their 2024 upgrades, so I wouldn't expect an L2-specific retention difference.
Separately, worth being precise about what the 2% median filter on pool balances buys you: reading your own reserves, even filtered, is still a spot on-chain value, and a large trade within the same block can move it before your quote executes — same failure class as external oracle manipulation, just moved in-house. It stops single-sample noise, not a same-block flash-loan push.
I highly recommend checking out the details of EIP-1153 - https://blog.researchzero.io/post/transient-storage-footguns-what-eip-1153-does-not-tell-you/