Why Use Triggers?
It is important to use triggers to make sure that assertions are only called when they are needed in order to save gas and resources. For example, it is a waste to trigger an assertion on every call to a contract if the assertion is only checking a value that can only be changed by a specific function. In this case it would make sense to use a trigger that only triggers on calls to the specific function or updates to the specific storage slot of the value.Trigger Behavior in Production
When a transaction is validated, the Credible Layer identifies all assertions with triggers matching the transaction. All matching assertions execute - if any fails, the transaction is dropped. This means:- Multiple assertions can protect the same function
- Each assertion with a matching trigger runs independently
- A transaction must pass all matching assertions to be included in a block
Trigger Types
Different triggers provide different guarantees and should be chosen based on the specific invariant you’re trying to enforce.Function Call Triggers
UseregisterFnCallTrigger when an assertion should run once for each successful call to a specific function. This is the preferred trigger for call-scoped checks because the assertion can read ph.context() and construct PhEvm.ForkId({forkType: 2, callIndex: ctx.callStart}) / PhEvm.ForkId({forkType: 3, callIndex: ctx.callEnd}) snapshots for the matched call.
- You only want to run assertions when specific functions are called
- You need to verify behavior of particular protocol operations
- You need call-scoped state reads or call metadata
Transaction End Triggers
UseregisterTxEndTrigger when an assertion should run after any transaction that touches the protected contract. This is the preferred trigger for transaction-wide invariants where the relevant mutation can happen through multiple functions or indirect calls.
- You need broad coverage across many entry points
- You only need transaction-scoped
ForkIdsnapshots - Function-specific triggers could miss an unexpected mutation path
Best Practices
Choose the Right Trigger
Consider what state changes your assertion monitors:- Use function call triggers when monitoring specific functions
- Use transaction end triggers for transaction-wide invariants
- Consider the trade-off between coverage and efficiency
Optimize for Performance
- Be as specific as possible with your triggers
- Avoid triggering on every transaction when a specific function/storage trigger suffices
- Multiple assertions with specific triggers are often better than one assertion with a broad trigger
Coverage Considerations
While triggers optimize performance, ensure they don’t create gaps in your security coverage:- Document which scenarios trigger your assertions
- Consider edge cases where state changes might occur through unexpected paths
- Test with various transaction patterns to verify trigger behavior
Related Documentation
Assertions Overview
Learn about assertions and how they work
Cheatcodes Reference
Complete API reference for trigger registration
Assertions Book
Real-world assertion examples and implementations
Testing Assertions
How to test your assertions and triggers

