What they watch
- Adopter: The contract protected by the assertion.
- Token: The ERC20 balance being watched.
- Direction: Tokens entering or leaving the adopter.
- Time: Balance changes across blocks and transactions.
Circuit breaker types
Cumulative outflow
Limits net tokens leaving the adopter over a rolling window.
Cumulative inflow
Limits net tokens entering the adopter over a rolling window.
Rate of change
Lets an assertion limit how quickly the watched balance changes.
How cumulative and rate limits work together
Cumulative and rate-of-change limits use different semantics. A cumulative limit measures total net flow over a rolling window, while a rate limit measures how quickly the balance changes. An assertion can enforce either policy or both. Both policies use a matching cumulative watcher to invoke the assertion. For a rate limit, the assertion reads the rolling-window rate data during that callback.
When checks are combined, the assertion can reject a transaction when either limit is exceeded.
Why use a Phylax circuit breaker
A protocol-integrated circuit breaker protects the execution paths wired through it. A Phylax circuit breaker runs as an assertion before the transaction settles. It watches the actual ERC20 balance change of the adopter.
Because enforcement runs as an assertion, the protocol does not need breaker-specific hooks or storage. The assertion can reject a candidate transaction when it exceeds the configured policy.
Bucket ingestion and balance reconstruction
On mainnet, the per-block buckets behind a cumulative watcher are not written while a block is being built. The executor ingests each complete canonical block after it has been executed, using the block’s ERC20Transfer logs and the adopter’s post-block token balance. Ingestion runs concurrently with block building, so bucket history can lag the chain tip and a candidate transaction can be validated before the buckets for one or more recent blocks exist. Ingested blocks can also land partway through building a block. Each transaction reads the latest ingested history at the time it is validated, so two transactions in the same block can be evaluated against different histories: an earlier one against reconstructed buckets and a later one against exact buckets.
We do not expect history to be behind by more than one block on a regular basis, and any practical window is far longer than that. The missing blocks are always the newest blocks before the candidate transaction, so they lie inside the rolling window. The rest of this section assumes that.
When the history is behind, the executor still runs the check. It reconstructs the missing buckets for that evaluation only:
- It derives the adopter’s token balance immediately before the candidate transaction from the current state and compares it with the balance recorded in the latest known bucket.
- It treats the difference as the net flow of the gap and spreads it evenly across the missing blocks, interpolating bucket timestamps between the last known block and the candidate block. If only earlier transactions in the current block are missing, the difference is added to the current block’s bucket instead.
- It evaluates the threshold against the reconstructed window plus the candidate transaction’s own transfers.
Effect on the circuit breaker trigger
When a transaction arrives at block N and the last stored bucket is block M with M < N-1, the blocks in between are missing. We pick the last stored bucket as the anchor, and we fill the gap like this:- Compare the anchor’s end-of-block balance with the candidate’s pre-transaction balance.
- Divide the difference evenly across the missing blocks.
- Splice those fake buckets into a clone of the state and evaluate on it. The fake buckets are thrown away and not persisted.
Effect on trigger context
Reconstruction recovers the exact net balance change, but not the individual transfers behind it. Inflows and outflows that offset each other within the gap cancel out and cannot be recovered.
If your circuit breaker uses
absoluteOutflow or absoluteInflow to get the total token flow values, please be aware that the values you are reading might be derived from the above mechanism and approximate. Please keep this in mind when designing the limits, as the gross figures can undercount.

