The TWAP oracle program (RFP-019) depends on the AMM pool exposing a price accumulator — a running record of the pool's price over time.
Currently PoolDefinition only stores the current reserves. This issue tracks the changes needed in the AMM program so RFP-019 has the data
it needs.
Background
A TWAP (Time-Weighted Average Price) is computed from two accumulator snapshots:
twap_tick = (tickCumulative[t2] - tickCumulative[t1]) / (t2 - t1)
tickCumulative is a running sum of current_tick × elapsed_seconds, updated at block boundaries before any trades execute. The current_tick is an integer encoding of the current spot price derived from the reserve ratio:
tick = floor( log(reserve_b / reserve_a) / log(1.0001) )
Computed via integer bit manipulation (MSB + iterative squaring) — no floating point.
What needs to be done
- Add accumulator fields to PoolDefinition in amm_core
pub struct PoolDefinition {
// existing fields ...
/// Current price tick, derived from reserve_b / reserve_a.
/// Updated on every state-changing operation.
pub current_tick: i32,
/// Timestamp (ms) at which current_tick was last recorded.
/// Used by the oracle program to advance tickCumulative.
pub last_observation_timestamp: u64,
}
- Compute and write
current_tick on every state change
Every instruction that modifies reserves must update current_tick and last_observation_timestamp before writing the pool post-state.
The tick must be computed from the post-swap reserves (the price after the trade, not before), because that is what the next block's
accumulator advance will be based on.
- Implement
tick_from_reserves(reserve_a, reserve_b) -> i32
Pure integer implementation in amm_core. No floating point. Approach:
- Represent price = reserve_b / reserve_a as a fixed-point integer
- Find floor(log2(price)) via 127 - leading_zeros() on the numerator
- Refine to the tick index using iterative squaring against the log(1.0001) constant (Uniswap v3 TickMath approach)
Acceptance criteria
- PoolDefinition contains current_tick: i32 and last_observation_timestamp: u64
- tick_from_reserves() is implemented in amm_core with unit tests covering known reserve ratios → expected ticks
- All state-changing instructions update both fields in their post-state output
- Existing tests remain green
- New tests cover: tick computed correctly after NewDefinition, tick updated after a swap that changes the reserve ratio
The TWAP oracle program (RFP-019) depends on the AMM pool exposing a price accumulator — a running record of the pool's price over time.
Currently
PoolDefinitiononly stores the current reserves. This issue tracks the changes needed in the AMM program so RFP-019 has the datait needs.
Background
A TWAP (Time-Weighted Average Price) is computed from two accumulator snapshots:
tickCumulativeis a running sum ofcurrent_tick × elapsed_seconds, updated at block boundaries before any trades execute. Thecurrent_tickis an integer encoding of the current spot price derived from the reserve ratio:Computed via integer bit manipulation (MSB + iterative squaring) — no floating point.
What needs to be done
current_tickon every state changeEvery instruction that modifies reserves must update current_tick and last_observation_timestamp before writing the pool post-state.
The tick must be computed from the post-swap reserves (the price after the trade, not before), because that is what the next block's
accumulator advance will be based on.
tick_from_reserves(reserve_a, reserve_b) -> i32Pure integer implementation in amm_core. No floating point. Approach:
Acceptance criteria