The contract has three independently-configured systems that can each restrict or reject a write call. This note lists them, their configuration surface, and — because none of them currently defers to the others — the precedence order that actually applies when more than one is in effect for a single call.
- States:
Normal,Degraded,Paused. - Config:
CircuitBreakerConfig(ttl_seconds,degraded_write_limit,auto_recover), set viaset_circuit_breaker_config. Pausedrejects every write withContractError::ContractPaused.Degradedallows writes but caps them atdegraded_write_limitper degraded window, rejecting the excess withContractError::CircuitBreakerDegradedLimitReached.- Auto-recovers back to
Normalonceauto_recover_athas passed (checked at the start of every call, before the state is used).
- Per-address sliding-window call budget:
max_callsperwindow_seconds. - A global default (
set_rate_limit_config) and an optional per-issuer override (set_issuer_rate_limit_config) — the per-issuer value wins when present. - An issuer can be added to a whitelist (
add_rate_limit_whitelist) that bypasses rate limiting entirely for that address. - Violating the limit rejects with
ContractError::RateLimitExceeded.
- Tracks calls-per-window and classifies the network as
Low,Normal, orHighload (CongestionLevel). update_congestion_and_adjust(an explicit, separately-invoked function — not run automatically on every call) recomputes the level and, if it changed, adjusts the global rate limit'smax_callsup or down by the configuredreduce_factor_bps/increase_factor_bps, clamped to[min_max_calls, max_max_calls].- Congestion does not gate a call by itself — it only reshapes system #2's
threshold, and only at the moments
update_congestion_and_adjustis called (e.g. by an operator or an off-chain keeper on a schedule).
For any single mutating call (e.g. issue_credential), the effective
checks — as they appear in Self::require_not_paused and the individual
entrypoints — run in this order:
- Circuit breaker auto-recovery check — may flip
Degraded/Pausedback toNormalbefore anything else is evaluated. - Circuit breaker
Pausedcheck — hard stop, applies unconditionally to every address, including whitelisted issuers and admins. - Circuit breaker
Degradedwrite-limit check — also unconditional; whitelisting an issuer for rate limiting does not exempt them from this cap. - Rate limiting (
require_rate_limit) — per-address/per-issuer check, using whatevermax_callscongestion throttling has most recently set. Whitelisted issuers skip this step entirely. - Congestion throttling is not a gate in this sequence — it only
matters insofar as it already adjusted step 4's
max_callsthe last timeupdate_congestion_and_adjustran.
In short: circuit breaker > rate limiting, and congestion only acts through rate limiting's configuration, never as its own gate.
- Contract is
Degraded, congestion level isLow. The degraded write cap (step 3) still applies. A priorLow-congestion adjustment may have raisedmax_calls, so more calls might pass rate limiting (step 4) before hitting the (unrelated) degraded-write cap. - An issuer is rate-limit-whitelisted, contract is
Degraded. The whitelist only bypasses step 4. Step 3's degraded write cap still applies to the whitelisted issuer's writes — whitelisting is not a circuit-breaker bypass.
get_throttling_status() returns a ThrottlingStatus snapshot combining:
- circuit breaker state, active-activation details, degraded write limit and current degraded write count,
- the global rate-limit
max_calls/window_seconds, - the current congestion level and its full
CongestionConfig.
This lets an operator or monitoring dashboard read the whole throttling
picture in one call instead of querying each system's individual getters
(get_circuit_breaker_state, get_circuit_breaker_config,
get_rate_limit_config_pub, get_congestion_level,
get_congestion_config, etc., which remain available individually).