Understand the Listener Contract
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.Mental Model
- A transaction is executed onchain.
- Sim IDX checks whether it matches any trigger you defined during deployment.
- When there’s a match, Sim IDX invokes the corresponding handler in your listener contract.
- The handler emits one or more events that capture the facts you care about.
- Sim IDX stores each event as a new row in the appropriate table of your app database.
File Anatomy
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.listeners/src/
.
Contract | Purpose | Location |
---|---|---|
Triggers | Registers all triggers via addTrigger . Must be named Triggers and defined in Main.sol . | listeners/src/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 listeners/src/ . | listeners/src/ |
Main.sol
file from the sample app down step-by-step.
Imports
Simidx.sol
provides core helpers, while Generated.sol
contains the Solidity code created from your ABIs.
The ./UniswapV3FactoryListener.sol
import brings in the listener contract from a separate file.
Triggers Contract
This contract tells Sim IDX when to run your code using a trigger, which specifies a target contract and the handler to call. TheTriggers
contract must be named Triggers
and must be located in listeners/src/Main.sol
.
You can also use helpers like
chainAbi
and chainGlobal
.
For other trigger types, see the Listener Features page.Main.sol
BaseTriggers
: An abstract contract fromSimidx.sol
that provides theaddTrigger
helper.triggers()
: The required function where you register all your triggers.new UniswapV3FactoryListener()
: The listener contract is instantiated from its own contract type.chainContract(...)
: This helper function uses theChains
enum for readability. The sample app registers the same trigger for Ethereum, Base, and Unichain, demonstrating how to monitor a contract across multiple networks.
Listener Contract
This is where you implement your business logic. The sample app places this logic inlisteners/src/UniswapV3FactoryListener.sol
.
UniswapV3FactoryListener.sol
- Inheritance: The listener extends an abstract contract (
UniswapV3Factory$OnCreatePoolFunction
) that is automatically generated from your ABI. This provides the required handler function signature and typed structs forinputs
andoutputs
. - Events: Emitting an event like
PoolCreated
defines the shape of your database.
UniswapV3FactoryListener
.
You should use descriptive names for your contracts.
For larger projects, you can even split logic into multiple listener contracts, each in its own .sol
file within the src/
directory.
Define and Emit Events
Events are the bridge between your listener’s logic and your database. When your listener emits an event, Sim IDX creates a database record.From Events to DB
The framework automatically maps your event to a database view. The event name is converted tosnake_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 |
Extending an Event
To capture more data, you simply add parameters to your event definition and update theemit
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
.
onCreatePoolFunction
handler.
pool_created
table will automatically include the new block_number
column.
Prefer
blockNumber()
over Solidity’s block.number
to standardize access across all supported blockchains.Trigger Onchain Activity
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:- Discover the Trigger: Find the abstract contract for your target function or event in the generated files.
- Extend the Listener: Add the abstract contract to your listener’s inheritance list.
- Define a New Event: Create a Solidity event to define your database schema.
- Implement the Handler: Write the function required by the abstract contract to process the data and emit your event.
- Register the Trigger: Call
addTrigger
in yourTriggers
contract to activate the trigger.
UniswapV3FactoryListener
contract.
We will extend the Listener
to also index the OwnerChanged
event from the Uniswap V3 Factory.
1. Discover the Trigger
Look insidelisteners/lib/sim-idx-generated/UniswapV3Factory.sol
. You will find an abstract contract for the OwnerChanged
event.
2. Extend the Listener
AddUniswapV3Factory$OnOwnerChangedEvent
to the inheritance list of the UniswapV3FactoryListener
contract in UniswapV3FactoryListener.sol
.
UniswapV3FactoryListener.sol
3. Define a New Event
Inside theUniswapV3FactoryListener
contract, add a new event to define the schema for the owner_changed
table.
4. Implement the Handler
Implement theonOwnerChangedEvent
function required by the abstract contract, also inside UniswapV3FactoryListener
.
5. Register the Trigger
Finally, add a new trigger for this handler in yourTriggers
contract within Main.sol
. You will need to instantiate the listener first.
Main.sol
Function Triggers
The framework supports both post-execution and pre-execution function triggers. Post-Execution: This is what the sample app uses withonCreatePoolFunction
. 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. This logic can live in its own file.
UniswapV3FactoryPreExecutionListener.sol
Triggers
contract inside Main.sol
.
Main.sol
Test Your Listener
Sim IDX gives you two ways to make sure your listener behaves as expected while you build.Unit Tests with Foundry
Thelisteners
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.
Historical Replay
Usesim 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.
Use the
--listeners
flag to specify which listener contract to evaluate. Set this to your contract’s name. When running evaluate
, the output may include events with names prefixed by fail_
which indicates data from failed or reverted transactions. For details on these outputs and available flags, see the sim listeners evaluate
documentation.Next Steps
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.Listener Features
Explore more listener features like triggering by ABI, global triggers, interfaces, and DB indexes.
Deployment Guide
Push your app to a staging or production environment and watch it process live onchain activity.
API Development Guide
Build fast, type-safe endpoints that surface the data your listener captures.