Cumulative compound index since inception (RAY). 1.0 == RAY == no growth.
fn index(env: soroban_sdk::Env, asset: soroban_sdk::Address) -> i128
Replace the contract's wasm (admin-gated). Address and storage are kept.
fn upgrade(env: soroban_sdk::Env, new_wasm_hash: soroban_sdk::BytesN<32>)
Index value (RAY) at an arbitrary past timestamp t.
A pinned t (see [AprOracle::pin_index]) is returned directly and stays available
forever; otherwise it is reconstructed from the observation buffer:
t newer than the latest observation -> the latest index (flat:
the index only moves on updates, so this is the stale-read value);t exactly on an observation -> that observation's index;t between two observations -> geometric interpolation
(constant-rate assumption between the surrounding snapshots);t in the future, or older than the oldest retained observation and not pinned,
or pool never updated -> panic.fn index_at(env: soroban_sdk::Env, asset: soroban_sdk::Address, t: u64) -> i128
Register a new pool/asset and start its index at 1.0 (RAY) "from inception", seeding the source baseline in the same call.
obs_capacity sizes the per-pool observation ring buffer (must be >= 1).
Creation-only: an asset that already has state is rejected. Re-configure would reset the
index to 1.0 and wipe the accrued history, so a fast-path (the governance deployer) may
register a new pool's asset without a timelock but can never disturb a live one.
Seeding here rather than leaving it to the first update_state closes the window between
the two: until a baseline exists the index cannot advance, so every bit of yield the source
earned in that gap was dropped, and a term starting inside it would settle short. It also
means the source is read under the admin's own call — a misconfigured or dead source fails
at registration instead of surfacing as a pool that quietly never accrues.
fn configure(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
obs_capacity: u32,
source: Source,
)
fn get_admin(env: soroban_sdk::Env) -> soroban_sdk::Address
Full state for a pool (debugging / off-chain reads).
fn get_state(env: soroban_sdk::Env, asset: soroban_sdk::Address) -> AprState
Durably memoize I(asset, t) so it survives eviction from the observation buffer — a
pure memoization of the oracle's own index series (nothing pool-specific). Permissionless
and idempotent (first write wins); a no-op for a future t or one already pinned. The
caller decides which timestamps to pin (e.g. a pool's term boundaries) and when.
Self-refreshing: if t is newer than the newest observation we would be extrapolating,
and buffer_index_at would take its flat branch and freeze the last known index —
silently dropping everything that accrued between it and t, for the life of the pin. We
are the only component that can tell, so we pull the source first and let t land between
two real observations. That costs one refresh, once, and only when the pin would otherwise
be wrong: an already-pinned t returns above, and a t already bracketed needs nothing (a
newer observation cannot change the pair surrounding it).
A t older than the oldest retained observation traps. Its history is gone
fn pin_index(env: soroban_sdk::Env, asset: soroban_sdk::Address, t: u64)
Transfer the admin role to new_admin (current admin only). The deploy hands the oracle to
the governance timelock this way once its owner-driven pool configuration is done — the same
handover the engine/vamm/lock-yield use.
fn set_admin(env: soroban_sdk::Env, new_admin: soroban_sdk::Address)
Accrued rate since pool inception as a RAY fraction (current_index - 1).
e.g. 0.05 * RAY means 5% has accrued since the pool started.
fn accrued_ray(env: soroban_sdk::Env, asset: soroban_sdk::Address) -> i128
Storage/schema version, bumped by a post-upgrade migration when the layout changes.
fn get_version(env: soroban_sdk::Env) -> u32
Advance a Source::GrowthIndex pool by pulling its source index and
deriving the rate from the index growth since the previous snapshot.
Deliberately permissionless (mirrors the public updateState in the
Solidity implementation): the caller only chooses the moment of the update,
the data comes from the configured source and cannot be forged.
fn update_state(env: soroban_sdk::Env, asset: soroban_sdk::Address)
fn __constructor(env: soroban_sdk::Env, admin: soroban_sdk::Address)
Accrued rate over [from, to] as a RAY fraction:
index(to) / index(from) - 1.
fn accrued_between(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
from: u64,
to: u64,
) -> i128
Read one observation slot (debugging / off-chain reads).
fn get_observation(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
slot: u32,
) -> Observation
Index growth I(to) / I(from) - 1 as a [Wad] (1e18): pool-agnostic, it only knows
two timestamps. Returns (growth, valid) and never traps — valid == false when
either endpoint is unavailable (unconfigured, future, or evicted-and-never-pinned), so
the caller surfaces a clean error rather than every future read for the pool tripping
once the buffer evicts the requested from. A pinned endpoint stays available
indefinitely (see [AprOracle::pin_index]); the caller pins the timestamps it needs to
outlive the buffer.
fn get_rate_from_to(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
from: u64,
to: u64,
) -> (Wad, bool)
Supply I(asset, t) by hand for a t the observation buffer no longer covers — the
recovery path for a [AprOracle::pin_index] that traps, and the only way an index value
ever enters this contract from outside its source.
Deliberately narrow, because it is the one place a human number can reach settlement:
t — while t is still in the buffer, pin_index derives the real
value and no judgement is needed, so the admin has no business there;t is rejected, so this can never rewrite a pin the
permissionless path produced honestly, and first-write-wins still holds;[RAY, I(oldest)] — the index starts at RAY and never decreases, so a
t before the oldest retained observation had an index between inception and that
observation's. Anything outside that range is provably wrong, whoever signed it.Those bounds leave real discretion inside the interval, which is why this is admin-gated (the governance timelock) rather th
fn set_pinned_index(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
t: u64,
index_ray: i128,
)