Docs

Hooks

TWAP oracle

A built-in hook that records the pool's tick around every swap so any program, or anyone off chain, can read a time-weighted mean over a recent window.

The TWAP hook turns a pool into an on-chain price record. Before and after every swap it accrues the tick the price sat at for the seconds since the last update. Reading a time-weighted average then costs one read-only call.

What it stores#

A fixed ring of 32 observations, each a timestamp, the cumulative tick up to that moment, and the tick that applied from then on. Updates in the same second replace the current tick instead of adding an entry, so the ring covers the last 32 seconds that saw a swap, however far back those reach.

History starts at the hook's first callback. A new pool can't answer a window older than its first swap.

Reading it#

Observe { seconds_ago } returns the arithmetic mean tick over the last seconds_ago seconds. It extrapolates the current tick up to the present Clock time, interpolates the window's start inside the retained history, and rounds the mean toward negative infinity.

The call takes two read-only accounts, the pool and its hook state, and answers with return data:

TwapReplyRust
pub struct TwapReply {
    pub version: u8,
    pub window_seconds: u32,
    pub start_timestamp: i64,
    pub end_timestamp: i64,
    pub mean_tick: i32,
    pub cumulative_tick: i128,
}
HTTP
GET /pools/{address}/twap?seconds=300

The API simulates Observe against finalized state and returns { windowSeconds, startTimestamp, endTimestamp, meanTick }. It never submits a transaction.

From tick to price#

A tick is a price on a fixed grid: price = 1.0001^tick, in atomic units of B per A. For a human price, adjust for decimals:

price (B per A) = 1.0001^mean_tick × 10^(decimals_A − decimals_B)

The mean of ticks is a geometric mean of prices. It is the pool's own record, not a fair-value feed: it is only as good as the liquidity and trading in this pool.

Windows it refuses#

  • Shorter than the pool's minimum window, set at creation (30 s in the app by default).
  • Older than the oldest retained observation.

Both fail with 7008 InsufficientHistory; the API answers TWAP_UNAVAILABLE.

Permissions#

The hook needs before swap (1) and after swap (2): mask 3. With before liquidity (4) and after liquidity (8) as well, it also records around deposits and withdrawals; the app offers that as "Also record the price when liquidity changes".