The core of a Sim IDX app is the listener, a Solidity contract that defines what onchain data to index. By writing simple handlers for specific contract function calls or events, you instruct the Sim IDX framework on which data to capture and store in your database.
This guide covers the structure of a listener contract, how to add indexing for new functions, and how to test your logic.
A listener is a special contract Sim IDX executes onchain. It has handler functions which are called when certain conditions are triggered onchain (e.g., when another contract calls a function, or a contract with a matching ABI emits an event). The Listener contract itself emits events which Sim IDX stores in your app’s database.
Before diving into listener development, make sure you understand the overall app folder structure and how the listeners/
folder fits into your Sim IDX app.
The indexing logic is primarily located in listeners/src/
.
Contract | Purpose | Location |
---|---|---|
Triggers | Registers all triggers via addTrigger . Contains no business logic. | Must be in Main.sol . |
Listener(s) | One or more contracts that implement handler logic and emit events. They can have any name and be defined in any .sol file within src/ . | listeners/src/ |
Let’s break the Main.sol
file from the sample app down step-by-step.
These two imports pull in everything provided by the Sim IDX framework. Simidx.sol
provides core helpers, while Generated.sol
contains the Solidity code created from your ABIs.
This contract tells Sim IDX when to run your code using a trigger, which specifies a target contract and the handler to call.
You can also use helpers like chainAbi
and chainGlobal
.
For other trigger types, see the Listener Patterns page.
BaseTriggers
: An abstract contract from Simidx.sol
that provides the addTrigger
helper.triggers()
: The required function where you register all your triggers.chainContract(...)
: This helper function uses the Chains
enum for readability. The sample app registers the same trigger for Ethereum, Base, and Unichain, demonstrating how to monitor a contract across multiple networks.This is where you implement your business logic. The sample app uses the name Listener
.
UniswapV3Factory$OnCreatePoolFunction
) that is automatically generated from your ABI. This provides the required handler function signature and typed structs for inputs
and outputs
.PoolCreated
defines the shape of your database.While the sample app uses the generic name Listener
, you can and should use more descriptive names for your contracts (e.g., UniswapV3Listener
). For larger projects, you can even split logic into multiple listener contracts, each in its own .sol
file within the src/
directory.
Events are the bridge between your listener’s logic and your database. When your listener emits an event, Sim IDX creates a database record.
The framework automatically maps your event to a database view. The event name is converted to snake_case
to become the view name, and each event parameter becomes a column.
For example, the PoolCreated
event from the sample app results in a queryable pool_created
view:
chain_id | caller | pool | token0 | token1 | fee |
---|---|---|---|---|---|
1 | 0x1f98431c8ad98523631ae4a59f267346ea31f984 | 0xf2c1e03841e06127db207fda0c3819ed9f788903 | 0x4a074a606ccc467c513933fa0b48cf37033cac1f | 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 10000 |
To capture more data, you simply add parameters to your event definition and update the emit
statement in your handler. Let’s modify the sample app to also record the block number.
1. Extend the Event Definition
Add the new blockNumber
parameter to your PoolCreated
event in Main.sol
.
2. Emit the New Data
Pass the new value when you emit the event in your onCreatePoolFunction
handler.
After deploying these changes, your pool_created
table will automatically include the new block_number
column.
Sim IDX can trigger on contract events as well as function calls, both before and after they execute. This allows you to capture a wide range of onchain activity.
To add a new trigger to your listener, you’ll follow a simple, five-step process:
addTrigger
in your Triggers
contract to activate the trigger.Let’s walk through an example of adding a new event trigger to the sample app’s Listener
contract.
We will extend the Listener
to also index the OwnerChanged
event from the Uniswap V3 Factory.
Look inside listeners/lib/sim-idx-generated/UniswapV3Factory.sol
. You will find an abstract contract for the OwnerChanged
event.
Add UniswapV3Factory$OnOwnerChangedEvent
to the inheritance list of the Listener
contract in Main.sol
.
Inside the Listener
contract, add a new event to define the schema for the owner_changed
table.
Implement the onOwnerChangedEvent
function required by the abstract contract, also inside Listener
.
Finally, add a new trigger for this handler in your Triggers
contract.
The framework supports both post-execution and pre-execution function triggers.
Post-Execution: This is what the sample app uses with onCreatePoolFunction
. The handler is called after the contract’s function completes, so it has access to both inputs
and outputs
.
Pre-Execution: To react to a function before it executes, you use the corresponding Pre-
abstract contract (e.g., preCreatePoolFunction
). The handler receives a PreFunctionContext
and only has access to the function’s inputs
, as outputs have not yet been generated.
Sim IDX gives you two ways to make sure your listener behaves as expected while you build.
The listeners
folder is a Foundry project. sim test
is a thin wrapper around forge test
. It will compile your contracts, execute all Forge unit tests inside listeners/test/
, and surface any failures.
Use sim listeners evaluate
to see how your listener reacts to real onchain data before pushing your updates. This command compiles your listener and executes the transactions in any block range you specify.
The --listeners=Listener
flag specifies which listener contract to evaluate. You can update this to match your specific listener contract name. Visit the sim listeners evaluate
documentation to learn more about the available flags.
You’ve now seen how to create triggers, emit events, and validate your listener. Here are a few great ways to level-up your Sim IDX app.
Learn about more advanced patterns for building your listener.
Push your app to a staging or production environment and watch it process live onchain activity.
Build fast, type-safe endpoints that surface the data your listener captures.