Skip to main content
In production, the Credible Layer drops transactions that fail assertions - they never enter the blockchain. This means there’s no on-chain trace of what the attacker tried or which vulnerability was targeted. In testing, failed assertions cause reverts. This lets you use vm.expectRevert() to verify your assertion catches violations and returns the expected error message. Understanding this difference is important for writing effective tests and interpreting their results.

Quick Comparison

Production: Transactions Are Dropped

In production, the Assertion Enforcer validates transactions during block building: Key behaviors:
  • Triggers are evaluated to determine which assertions run
  • All matching assertions for all interacted contracts execute
  • Failed transactions are dropped - they never enter the blockchain
  • Users don’t see reverts; the transaction simply doesn’t get included

Testing: Transactions Revert

In testing, cl.assertion() registers an assertion to run on the next external call: Key behaviors:
  • You specify the assertion function via fnSelector, but triggers still determine if it runs
  • Only the specified assertion runs, not all assertions for the contract
  • Only the next external call is validated, then the registration is consumed
  • Reverts simulate drops - use vm.expectRevert() to test failure cases

The cl.assertion() Mental Model

cl.assertion() works like vm.prank() - it only affects the immediately following external call.

State Persistence

When an assertion passes, state changes persist for the rest of the test. You can assert on updated values and register subsequent assertions.

Common Pitfalls

”Expected 1 assertion to be executed, but 0 were executed”

This error means the assertion didn’t run. Common causes:
  1. External call between cl.assertion() and target - the intervening call consumed the assertion registration
  2. Called function doesn’t match a registered trigger - see below
  3. Target transaction reverts before assertion runs - the protocol function itself fails

Trigger Mismatch

The fnSelector parameter specifies which assertion function to run, but triggers are still evaluated. The assertion only runs if the next external call matches a registered trigger for that assertion function.
To verify triggers work correctly with real transactions, use backtesting.

Testing Assertions

Testing patterns

Triggers

Production trigger behavior

Backtesting

Test with real transactions

Troubleshooting

Common errors