Initialise the oracle. Idempotent against re-init: a second call
reverts with AlreadyInitialized.
Validates:
max_delta_per_sync_wad > 0 && < WAD (a 100%+ allowed delta
would disable the rate-limit entirely).observation_window >= 2 (a 1-slot buffer cannot do TWAP).max_staleness_ledgers > 0.Soroban's __constructor runs once on register(). We treat the
admin arg as the multisig/timelocked controller (ADR-002).
fn __constructor(env: soroban_sdk::Env, admin: soroban_sdk::Address, cfg: OracleConfig)
Refresh the cached rate from Blend.
Permissionless: anyone can prod the oracle. Validates
monotonicity and the per-sync delta cap; on violation the oracle
freezes, the violation is emitted as an event, and the
last-known-good cached rate is returned. The freeze flag
must persist across the call, so this function deliberately
returns Ok(cached_rate) instead of Err: a Soroban Err
return rolls back storage writes, which would defeat the freeze.
Consumers must check is_frozen() after sync_rate and refuse
to proceed if it returns true.
Returns:
Ok(new_rate) on a clean sync.Ok(cached_rate) (the last-known-good rate) on a violation;
the oracle is frozen as a side effect.Err(Paused) if the oracle is paused.Err(OracleRejected) only if the oracle was already frozen
before the call, or Blend returned a non-positive rate.fn sync_rate(env: soroban_sdk::Env) -> Result
Push an observation into the TWAP ring buffer.
Called by the AMM on each swap. Auth is not enforced at the contract level (the AMM signs the call as itself, which is the only address that should bother); the worst an adversary can do by spamming is write their own price into the buffer, which the time-weighted formula naturally amortises — and the AMM's lock against same-block reads (ADR-001 §4) eliminates the single-block manipulation vector.
fn update_observation(
env: soroban_sdk::Env,
price: i128,
) -> Result<(), soroban_sdk::Error>
View: latest cached rate, WAD-scaled. Reverts with OracleStale
if the cache hasn't been refreshed within the configured
staleness window. Consumers should call sync_rate and retry.
fn get_rate(env: soroban_sdk::Env) -> Result
View: TWAP across the populated portion of the ring buffer.
Reverts with OracleStale if fewer than 2 observations are
recorded (TWAP is undefined). The weight of each observation is
ts[i+1] - ts[i]; the final observation runs to now.
fn get_twap(env: soroban_sdk::Env) -> Result
View: latest single observation (price, ts). Convenience for
indexers; protocol logic should prefer get_twap.
fn latest_observation(env: soroban_sdk::Env) -> Result<(i128, u64), soroban_sdk::Error>
View: whether the oracle is frozen.
fn is_frozen(env: soroban_sdk::Env) -> bool
View: whether the oracle is paused.
fn is_paused(env: soroban_sdk::Env) -> bool
Admin-only: pause the oracle. Instant. While paused, all sync /
read / observation paths revert with Paused.
fn pause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Admin-only: propose unpause. Records a 48h-eta proposal; the asymmetric pause from ADR-002 forbids instant unpause.
fn propose_unpause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Permissionless after eta: finalize the pending unpause.
fn execute_unpause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Admin-only: propose a re-attestation. Records (attested_rate, eta = now + 48h). After the eta, execute_reattest finalises
the operation, writes the attested rate as the new cached rate,
and clears the frozen flag.
Repeated calls overwrite the pending proposal (and reset the timer), so the admin can revise the attested rate before execution. This is desirable: between proposal and execution, the admin may observe Blend's true rate moving and want to update.
fn reattest(
env: soroban_sdk::Env,
attested_rate: i128,
) -> Result<(), soroban_sdk::Error>
Cancel a pending reattest proposal. Admin-only.
fn cancel_reattest(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Permissionless: execute a previously-proposed reattest after its
eta. Unfreezes the oracle and overwrites the cached rate.
fn execute_reattest(env: soroban_sdk::Env) -> Result
View: the active config.
fn config(env: soroban_sdk::Env) -> OracleConfig
View: the current admin address.
fn admin(env: soroban_sdk::Env) -> soroban_sdk::Address
View: the cached rate and its timestamp without applying the
staleness gate. Returns (0, 0) if never synced.
fn cached_rate_raw(env: soroban_sdk::Env) -> (i128, u64)
View: how many observations are currently populated.
fn observation_count(env: soroban_sdk::Env) -> u32