Trigger on an ABI
ThechainAbi
helper allows you to trigger your listener on any contract that matches a specific ABI signature. This is incredibly powerful for monitoring activity across all instances of a particular standard, like ERC-721 or Uniswap V3 pools, without needing to list every contract address explicitly.
ABI Matching is Permissive: The matching behavior is permissive - if a contract implements the functions and events in the specified ABI, it counts as a match even if it also implements other functionality. This means contracts don’t need to match the ABI exactly; they just need to include the required functions and events.
onBurnEvent
handler for any contract on Ethereum that matches the UniswapV3Pool
ABI. The UniswapV3Pool$Abi()
is a helper struct that is automatically generated from that ABI file.
Main.sol
UniswapPoolListener.sol
Trigger Globally
ThechainGlobal
helper creates triggers that are not tied to any specific contract or ABI. This can be used to set up block-level handlers with onBlock
for tasks that need to run once per block, such as creating periodic data snapshots, calculating time-weighted averages, or performing end-of-block settlements.
The framework provides a built-in abstract contract, Raw$OnBlock
, for this purpose.
First, implement the onBlock
handler and register the trigger in the Triggers
contract.
Next, add Raw$OnBlock
to your listener’s inheritance list.
Main.sol
MyBlockListener.sol
Raw$OnCall
and Raw$OnLog
, allowing you to create global triggers for every function call or every event log on a chain.
Register Multiple Triggers
addTriggers
(plural) lets you register several handler functions for the same contract, ABI, or global target in one call. It accepts an array of trigger functions.
addTriggers
lives in the same Triggers
contract as addTrigger
. It is purely a convenience helper and behaviour is identical. If you are new to triggers, start with the Listener Basics guide where addTrigger
is introduced.listeners/src/Main.sol
addTriggers
when your listener exposes multiple handlers that share a target.
Use Interfaces
Often, your handler is triggered by one contract, but you need to fetch additional data from another contract to enrich your event. For example, aSwap
event on a pool tells you a swap occurred, but you need to call the pool contract directly to get its current slot0
state. Solidity interfaces allow your listener to do this.
1. Define the Interface
It’s best practice to create aninterfaces
directory (e.g., listeners/src/interfaces/
) and define the interface in a new .sol
file.
listeners/src/interfaces/IUniswapV3Pool.sol
2. Import and Use the Interface
In your listener, import the interface. You can then cast a contract’s address to the interface type to call its functions.For guidance on resolving compilation issues such as name conflicts or
Stack too deep
errors, refer to the Listener Errors guide.DB Indexes
Database indexes are a common way to improve database performance. Sim IDX lets you define indexes directly on the event definition of your listener contract, giving you fine-grained control of your database’s performance.Learn More About PostgreSQL Database Indexes
To learn more about database indexes, visit the PostgreSQL documentation.
Index Definition Syntax
To add a database index, use a special comment with the@custom:index
annotation directly above the event
definition in your Solidity listener.
<index_name>
: A unique name for your index.<INDEX_TYPE>
: The type of index to create (e.g.,BTREE
,HASH
).(<columns>)
: A comma-separated list of one or more columns to include in the index. These names must exactly match the field names in your event or struct definition, including case.
Adding Indexes to Events with Unnamed Structs
Adding Indexes to Events with Unnamed Structs
When events use unnamed structs to avoid Solidity’s “Stack too deep” errors, column names in your index annotations must match the struct field names, not event parameter names.Notice how the index column names (
poolId
, blockNumber
, blockTimestamp
, etc.) correspond exactly to the field names in the SwapData
struct, even though the event takes an unnamed parameter.Adding Indexes to Events with Named Parameters
Adding Indexes to Events with Named Parameters
For simple events with fewer parameters that don’t hit Solidity’s stack limits, you can use named parameters directly. Column names must match the event parameter names.
Multiple Indexes Per Event
You can define multiple indexes for a single event by adding multiple@custom:index
lines. This is useful when you want to query the same table in different ways.
When events use unnamed structs to solve Solidity’s stack errors, Sim IDX automatically flattens the struct fields into individual database columns. Your index annotations reference these struct field names.
Supported Index Types
Sim IDX supports several PostgreSQL index types.BTREE
is the default and most common type, suitable for a wide range of queries.
Type | Use Case |
---|---|
BTREE | The default and most versatile index type. Good for equality and range queries on sortable data (= , > , < , BETWEEN , IN ). |
HASH | Useful only for simple equality comparisons (= ). |
BRIN | Best for very large tables where columns have a natural correlation with their physical storage order (e.g., timestamps). |
GIN | An inverted index useful for composite types like array or jsonb . It can efficiently check for the presence of specific values within the composite type. |
Learn More About PostgreSQL Index Types
To learn more about index types and their specific use cases, visit the
PostgreSQL documentation.
Syntax Validation
Thesim build
command automatically validates your index definitions. If it detects an error in the syntax, it will fail the build and provide a descriptive error message.
For example, if you misspell a column name:
Error: Cannot find column(s): 'block_numbr' in event PositionOwnerChanges
Block Ranges
You can specify custom block ranges for your triggers to target specific blocks or time periods. This is particularly useful for historical data analysis, testing specific time periods, or limiting triggers to certain blockchain events. Chain helper functions support.withStartBlock()
, .withEndBlock()
, and .withBlockRange()
methods:
Arbitrum One Pre-Nitro Limitation
Sim IDX does not support blocks on Arbitrum One created before the Nitro upgrade. Any triggers configured with a start block earlier than the Nitro boundary (block 22,207,818, which occurred on 2022-08-31) will not be able to process pre-Nitro blocks.
22207818
or greater: