What you’ll learn in this guide
- How the Quoter pattern lets an onchain listener pull fully-decoded swap data straight from Uniswap X reactors. This is something conventional offchain indexers can’t do.
- Why a single Sim IDX listener can index every Uniswap X swap, regardless of reactor type or order variant.
- The fee-handling and onchain interaction techniques that make your indexer both accurate and future-proof.
Why is Uniswap X Hard to Index?
The core difficulty in indexing Uniswap X stems from its order-based architecture. Reactors settle signed orders and verify execution, but they don’t emit explicit logs of the final token amounts. Instead, the critical details live inside encoded calldata that only the reactors themselves can decode. This complexity is compounded by:- Multiple Reactor Types: Uniswap X relies on several reactor contracts, each tailored to a different kind of order with its own decoding logic. An indexer must replicate the nuances of every variant, which is a significant engineering challenge.
- Opaque Logs: onchain events expose only an order hash and the filler address, leaving out the tokens traded, the final amounts, and even the recipient of the swap proceeds.
- Decoded State is Private/Internal: All the logic required to understand an order’s final state is contained within the reactor contracts themselves. Traditional indexers, which operate offchain, cannot easily access or execute this onchain logic.
The Sim IDX Solution
Sim IDX overcomes these challenges by fundamentally changing the relationship between the indexer and the protocol. Where traditional indexers are passive, offchain observers, a Sim IDX listener is a smart contract that lives onchain. The listener is a smart contract, so it can do more than just read events. It can interact with other onchain contracts. The listener can call their functions, pass them arguments, and read their return data, just like any other contract would. This capability lets us use an elegant technique we call the Quoter Pattern. Instead of painstakingly re-implementing Uniswap X’s sophisticated decoding logic, we simply ask the Reactor to run its own logic for us. We treat the protocol itself as an on-demand decoding oracle. The following sections highlight the most unique parts of this implementation. To see the code in its entirety, we encourage you to explore the full repository.View Full Code on GitHub
Explore the complete, unabridged source code for this Uniswap X indexer.
Index by ABI, Not by Address
The first challenge in indexing Uniswap X is its scale. The protocol uses multiple Reactor contracts across several chains, and new ones can be deployed at any time. Maintaining a hardcoded list of addresses is not a scalable solution. Sim IDX solves this with ABI-based triggers. Instead of targeting specific addresses, you can instruct your listener to trigger on any contract that matches a given ABI signature. This makes your indexer automatically forward-compatible. We achieve this with theChainIdAbi
helper in our Triggers
contract. The code below sets up our listener to trigger on the execute
function family for any contract on Ethereum, Unichain, and Base that implements the IReactor
interface.
listeners/src/Main.sol
To learn more about
ChainAbi
, see the Listener Features guide.Decode Opaque Data with the Quoter Pattern
Once triggered, our listener receives the opaqueorder
bytes. Replicating the decoding logic for every reactor type offchain would be a massive, brittle engineering effort.
With Sim IDX, we don’t have to. Because our listener is an onchain contract, we can use the “Quoter Pattern” to have the Reactor contract decode its own data for us.
This pattern is implemented in the OrderQuoter.sol
contract, which our main Listener
inherits from. It revolves around two primary functions:
-
The
quote
function, called by our listener’s handler, simulates a fill by calling the Reactor’sexecuteWithCallback
inside atry/catch
block. It knows this call will revert and is designed to catch the revert reason.listeners/src/OrderQuoter.sol -
The
reactorCallback
function is the endpoint the Reactor calls on our contract. It receives the fully decodedResolvedOrder
, encodes it, and immediately places it into arevert
message.listeners/src/OrderQuoter.sol
Build the Perfect Event Onchain
After decoding the raw order, we need to refine it into a complete and useful record. The listener acts as an onchain data assembly line, composing other onchain logic for accuracy and making live calls to enrich the data. First, to ensure financial accuracy, we must account for protocol fees. We do this by porting Uniswap’s own onchain fee logic into aFeeInjector
library. Our handler calls this library to inject any applicable fees into the ResolvedOrder
, ensuring the final amounts are perfect.
listeners/src/Main.sol
call
to each token contract to retrieve its metadata.
listeners/src/Main.sol
Define Your API Directly in Solidity
The final step in the Sim IDX workflow demonstrates its most powerful abstraction: your onchainevent
definition is your API schema.
In our Listener
contract, we define a SwapData
struct and a Swap
event. This struct contains all the decoded, fee-corrected, and enriched data points we assembled in the previous steps.
listeners/src/Main.sol
Swap
event is emitted, Sim IDX automatically creates a swap
table in your database with columns matching the fields in SwapData
. It then generates a type-safe Drizzle schema for you to use in your API.
The result is that your API code becomes trivially simple. You can query your swap
table without writing any complex data transformation pipelines or ORM configurations.
apis/src/index.ts