pub enum ConfigError {
InvalidDeviationBps,
InvalidStalenessSeconds,
InvalidLiquidityThreshold,
InvalidCrossSourceBps,
InvalidHaltLedgers,
InvalidTradeCountThreshold,
InvalidSnapshotAge,
InvalidPreviousStalenessSeconds,
}Expand description
Errors returned by SafeOracleConfig::validate when a config field
has an out-of-range value that would silently disable a guardrail or
produce nonsensical behavior at runtime.
§Spec
See spec §4 — Config Struct. Validation prevents silent guardrail
disabling caused by misconfiguration (e.g., max_deviation_bps = 0
allows infinite deviation, effectively disabling the deviation check
without any visible signal).
§Audit notes
-
Validation is opt-in — callers must invoke
config.validate(). The library does not enforce validation inlastprice()to avoid per-call gas cost. Production integrators should validate at init time (recommended pattern:MockLending::initializecalls validate). -
All errors are recoverable at init time; runtime config changes are not supported (config is immutable after deploy per spec §4).
Variants§
InvalidDeviationBps
max_deviation_bps is 0 (allows infinite deviation, disabling the
check) or > 10_000 (100% — values above this are nonsensical for a
relative-deviation threshold).
InvalidStalenessSeconds
max_staleness_seconds is 0 (rejects every recorded price as
stale) or > 86_400 (24h — stale data older than a day is unsafe
regardless of how lenient the integrator wants to be).
InvalidLiquidityThreshold
min_liquidity_usd is <= 0 — negative values are semantically
nonsense (liquidity is non-negative by definition), and 0 silently
disables the Layer 2 liquidity check (every snapshot’s
volume_30m_usd > 0 trivially passes the threshold). Same defensive
principle as InvalidTradeCountThreshold: a zero guardrail is no
guardrail.
§AR.H M1 closure
This variant’s runtime rule was tightened from < 0 to <= 0 after
AR.H surfaced the silent-disable case as the single residual asymmetry
from Hardening Closure (Debt #22).
InvalidCrossSourceBps
max_cross_source_bps is 0 (requires impossible primary/secondary
price equality) or > 10_000 (semantically nonsensical, > 100% deviation)
when secondary_oracle is configured. Validation skipped if secondary
is None (field is dormant).
§AR.H L2 closure
Validation rule was tightened from > 10_000 only to == 0 || > 10_000
after AR.H surfaced the silent-footgun case where a zero threshold
produces always-fires CrossSourceMismatch on every borrow.
InvalidHaltLedgers
circuit_breaker_halt_ledgers is 0 (degenerate halt window — the
breaker would fire and immediately auto-recover, providing no actual
halt) or > MAX_CIRCUIT_BREAKER_HALT_LEDGERS (~1 week, beyond the
reasonable auto-recovery window) when circuit_breaker_enabled is
true. Validation skipped if breaker is disabled (field is dormant).
§AR.H L1 closure
Upper bound added after AR.H surfaced that u32::MAX (~6.8 years
at Stellar’s ledger cadence) makes a misconfigured deploy
effectively-permanently halted without governance intervention.
InvalidTradeCountThreshold
min_trade_count_1h is 0 — disables thin-sampling check entirely
(every snapshot’s unique_trades_1h >= 0 always true). Same defensive
principle as InvalidDeviationBps: a “guardrail of zero” is no
guardrail at all. Integrators wanting to disable Layer 2 thin-sampling
should leave the guardrail’s threshold meaningful and route around it
via Layer 1 / circuit breaker controls.
§Hardening 3A follow-up (Debt #22)
This variant closes the gap intentionally left open in Hardening 3A, where the prompt’s “5 variant” boundary kept the pattern consistent with the other guardrails but left two silent-disable cases undetected. Hardening Closure brings parity.
InvalidSnapshotAge
max_snapshot_age_seconds is 0 (rejects all snapshots) or > 86_400
(24h — staler than this is unsafe regardless of integrator intent).
Mirrors InvalidStalenessSeconds boundary logic for the Layer 2 path.
§Hardening 3A follow-up (Debt #22)
Same closure rationale as InvalidTradeCountThreshold. The Hardening
3A boundary kept the new-variant count at 5; the Layer 2 snapshot age
validation remained an audit-trail gap until this patch.
InvalidPreviousStalenessSeconds
previous_max_staleness_seconds == 0 silently disables the
previous-price freshness check (every previous price would be
classified StaleData, blocking every borrow), or > 86_400 (24h)
accepts unsafe staleness for the deviation reference. Mirrors
InvalidStalenessSeconds boundary logic.
Phase 7.2 addition — pairs with the new
previous_max_staleness_seconds field on SafeOracleConfig.
Implementations§
Trait Implementations§
Source§impl Clone for ConfigError
impl Clone for ConfigError
Source§fn clone(&self) -> ConfigError
fn clone(&self) -> ConfigError
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ConfigError
impl Debug for ConfigError
Source§impl PartialEq for ConfigError
impl PartialEq for ConfigError
Source§fn eq(&self, other: &ConfigError) -> bool
fn eq(&self, other: &ConfigError) -> bool
self and other values to be equal, and is used by ==.Source§impl TryFromVal<Env, &ConfigError> for Val
impl TryFromVal<Env, &ConfigError> for Val
type Error = ConversionError
fn try_from_val(env: &Env, val: &&ConfigError) -> Result<Self, ConversionError>
Source§impl TryFromVal<Env, ConfigError> for Val
impl TryFromVal<Env, ConfigError> for Val
type Error = ConversionError
fn try_from_val(env: &Env, val: &ConfigError) -> Result<Self, ConversionError>
Source§impl TryFromVal<Env, Val> for ConfigError
impl TryFromVal<Env, Val> for ConfigError
type Error = ConversionError
fn try_from_val(env: &Env, val: &Val) -> Result<Self, ConversionError>
impl Eq for ConfigError
impl StructuralPartialEq for ConfigError
Auto Trait Implementations§
impl Freeze for ConfigError
impl RefUnwindSafe for ConfigError
impl Send for ConfigError
impl Sync for ConfigError
impl Unpin for ConfigError
impl UnsafeUnpin for ConfigError
impl UnwindSafe for ConfigError
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for C
impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for C
type Error = E
fn compare( &self, a: &(T, U, V, W), b: &(T, U, V, W), ) -> Result<Ordering, <C as Compare<(T, U, V, W)>>::Error>
Source§impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for C
impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for C
type Error = E
fn compare( &self, a: &(T, U, V, W, X), b: &(T, U, V, W, X), ) -> Result<Ordering, <C as Compare<(T, U, V, W, X)>>::Error>
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<E, T, U> FromVal<E, T> for Uwhere
E: Env,
U: TryFromVal<E, T>,
impl<E, T, U> FromVal<E, T> for Uwhere
E: Env,
U: TryFromVal<E, T>,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more