pub enum PriceResult {
Ok(PriceData),
Err(u32),
}Expand description
Result type for lastprice() that allows auto-halt to commit even on
guardrail violations.
§Why a custom enum instead of Result<PriceData, OracleSafetyViolation>?
Soroban contract methods that return Result::Err cause all storage
writes in the same invocation to roll back, including writes inside
open_circuit_breaker(). The original Phase 5.2 design hit this and was
reverted (commit e98ed48). By returning Ok(PriceResult::Err(...))
from the contract method, the breaker write commits while still
conveying the violation to the caller.
§Why Err(u32) and not Err(OracleSafetyViolation)?
OracleSafetyViolation is a #[contracterror] type. soroban-sdk 25.x
has two distinct constraints that block embedding it inside the
#[contracttype] enum below — both empirically verified, the second
not surfaced until Hardening 6C’s PoC:
-
SorobanArbitrarybound (Pre-5.4 finding). Under the test featuresoroban-sdkderives anArbitraryprototype for every#[contracttype]. The derive recursively requires every variant’s payload to implementSorobanArbitrary, which#[contracterror]types do not — build fails with “trait boundOracleSafetyViolation: SorobanArbitraryis not satisfied.” ManualSorobanArbitraryimpl on the error type is conceptually possible (the trait ispub, three trait bounds to satisfy). -
ScVec: TryFrom<(ScSymbol, &OracleSafetyViolation)>bound (Hardening 6C finding, deferred). Independent of theArbitraryderive, the#[contracttype]macro’s XDR encoding expects each variant payload to be convertible into the tuple shape(ScSymbol, &T)⟶ScVec.#[contracterror]types implementIntoVal<Env, Val>but not this specific tuple-to-XDR path. A manual impl is blocked by Rust’s orphan rule — bothScVecand(ScSymbol, &T)are foreign, so neither side of theTryFromcan host the impl from this crate. Closing this would require either (a) asoroban-sdkchange exposing the conversion or (b) reshapingOracleSafetyViolationaway from#[contracterror](which would lose the stable u32 discriminants that integrators consume).
Carrying the violation as its u32 discriminant sidesteps both
constraints. The values here MUST stay aligned with
OracleSafetyViolation = 1..=10. The into_result() shim re-hydrates
the typed variant for callers that want it. Hardening Phase debt #17
remains deferred for future SDK releases that resolve constraint (2).
§Migration from the Phase 1-4 Result<PriceData, OracleSafetyViolation>
Callers that used ? continue to do so via the into_result() shim:
// Before (Phase 1-4):
let price = safe_oracle::lastprice(&env, &asset, ...)?;
// After (Phase 5.2 v2):
let price = safe_oracle::lastprice(&env, &asset, ...).into_result()?;From<Result<PriceData, OracleSafetyViolation>> is also implemented so
internal helpers that produce Result (e.g., lastprice_inner) convert
at the API boundary without per-callsite match plumbing.
§Audit notes
PriceResult::Err(d)is semantically identical to a guardrail failure. A lending protocol MUST NOT proceed withPriceResult::Errthe same way it would not proceed withErrin Phase 1-4.- The
Okwrapping at the Soroban boundary is a storage-commit mechanism only; the public-facing semantics (“violation = no price”) are unchanged. - Tuple variants (not named-field) match the soroban-sdk 25.x
#[contracttype]enum constraint observed in Phase 5.1.
§Spec
See spec §4 — Function Signature and Stub Contract. PriceResult
preserves the spec’s lastprice → Ok(price) | Err(violation) semantic
at the public API level (via PriceResult::into_result) while letting
auto-halt writes inside lastprice commit at the Soroban boundary.
Variants§
Ok(PriceData)
Validated price data, all guardrails passed.
Err(u32)
Guardrail violation; price MUST NOT be used. The u32 is the
OracleSafetyViolation discriminant (1..=10); see into_result()
for the typed re-hydration.
Implementations§
Source§impl PriceResult
impl PriceResult
Sourcepub fn into_result(self) -> Result<PriceData, OracleSafetyViolation>
pub fn into_result(self) -> Result<PriceData, OracleSafetyViolation>
Convert to standard Rust Result for ergonomic ? operator usage.
Recommended migration path for Phase 1-4 callers: replace
lastprice(...)? with lastprice(...).into_result()?.
Re-hydrates the u32 discriminant into the typed
OracleSafetyViolation. Unknown discriminants panic — they cannot
occur on a result produced by lastprice(), which only emits
values from the canonical 1..=10 range, but the explicit panic
guards against forged values reaching the shim.
Trait Implementations§
Source§impl Clone for PriceResult
impl Clone for PriceResult
Source§fn clone(&self) -> PriceResult
fn clone(&self) -> PriceResult
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 PriceResult
impl Debug for PriceResult
Source§impl From<Result<PriceData, OracleSafetyViolation>> for PriceResult
impl From<Result<PriceData, OracleSafetyViolation>> for PriceResult
Source§impl PartialEq for PriceResult
impl PartialEq for PriceResult
Source§fn eq(&self, other: &PriceResult) -> bool
fn eq(&self, other: &PriceResult) -> bool
self and other values to be equal, and is used by ==.Source§impl TryFromVal<Env, &PriceResult> for Val
impl TryFromVal<Env, &PriceResult> for Val
type Error = ConversionError
fn try_from_val(env: &Env, val: &&PriceResult) -> Result<Self, ConversionError>
Source§impl TryFromVal<Env, PriceResult> for Val
impl TryFromVal<Env, PriceResult> for Val
type Error = ConversionError
fn try_from_val(env: &Env, val: &PriceResult) -> Result<Self, ConversionError>
Source§impl TryFromVal<Env, Val> for PriceResult
impl TryFromVal<Env, Val> for PriceResult
type Error = ConversionError
fn try_from_val(env: &Env, val: &Val) -> Result<Self, ConversionError>
impl Eq for PriceResult
impl StructuralPartialEq for PriceResult
Auto Trait Implementations§
impl Freeze for PriceResult
impl RefUnwindSafe for PriceResult
impl Send for PriceResult
impl Sync for PriceResult
impl Unpin for PriceResult
impl UnsafeUnpin for PriceResult
impl UnwindSafe for PriceResult
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