Why Use Triggers?
Scoped triggers reduce execution cost. Choose a trigger that covers every path that can violate the policy.Trigger Behavior in Production
Each matching assertion executes independently. A participating builder excludes a candidate if any active production assertion fails.Trigger Types
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.
Example:
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.
Example:
State and Flow Triggers
Choose a narrower trigger when it still covers every mutation that matters:
Storage triggers remain supported in current patterns. Use
registerFnCallTrigger instead of legacy registerCallTrigger for new call-scoped policies. See the library reference for helper signatures and specification requirements.
Best Practices
Test that the trigger covers all relevant state changes, including indirect calls and alternative entry points. Narrow it only when those tests still pass.Related Documentation
Assertions Overview
Learn about assertions and how they work
Cheatcodes Reference
Complete API reference for trigger registration
Use Cases Book
Real-world assertion examples and implementations
Testing Assertions
How to test your assertions and triggers

