1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
/// Oracle provides price and liquidity data useful for a wide variety of system designs
///
/// Instances of stored oracle data, "observations", are collected in the oracle array,
/// represented as PDAs with array index as seed.
///
/// Every pool is initialized with an oracle array length of 1. Anyone can pay to increase the
/// max length of the array, by initializing new accounts. New slots will be added when the
/// array is fully populated.
///
/// Observations are overwritten when the full length of the oracle array is populated.
///
/// The most recent observation is available, independent of the length of the oracle array,
/// by passing 0 as the index seed.
///
use anchor_lang::prelude::*;
/// Seed to derive account address and signature
pub const OBSERVATION_SEED: &str = "o";
/// Returns data about a specific observation index
///
/// PDA of `[OBSERVATION_SEED, token_0, token_1, fee, index]`
///
#[account(zero_copy)]
#[derive(Default)]
#[repr(packed)]
pub struct ObservationState {
/// Bump to identify PDA
pub bump: u8,
/// The element of the observations array stored in this account
pub index: u16,
/// The block timestamp of the observation
pub block_timestamp: u32,
/// The tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp
pub tick_cumulative: i64,
/// The seconds per in range liquidity for the life of the pool as of the observation timestamp
pub seconds_per_liquidity_cumulative_x32: u64,
/// Whether the observation has been initialized and the values are safe to use
pub initialized: bool,
}
impl ObservationState {
/// Transforms a previous observation into a new observation, given the passage of time
/// and the current tick and liquidity values
///
/// # Arguments
///
/// * `last` - Must be chronologically equal to or greater than last.blockTimestamp,
/// safe for 0 or 1 overflows.
/// * `block_timestamp` - The timestamp of the new observation
/// * `tick` - The active tick at the time of the new observation
/// * `liquidity` - The total in-range liquidity at the time of the new observation
///
pub fn transform(self, block_timestamp: u32, tick: i32, liquidity: u64) -> ObservationState {
let delta = block_timestamp - self.block_timestamp;
ObservationState {
bump: self.bump,
index: self.index,
block_timestamp,
tick_cumulative: self.tick_cumulative + tick as i64 * delta as i64,
seconds_per_liquidity_cumulative_x32: self.seconds_per_liquidity_cumulative_x32
+ ((delta as u64) << 32) / if liquidity > 0 { liquidity } else { 1 },
initialized: true,
}
}
/// Writes an oracle observation to the account, returning the updated cardinality.
/// Writable at most once per second. Index represents the most recently written element.
/// cardinality and index must be tracked externally.
/// If the index is at the end of the allowable array length (according to cardinality),
/// and the next cardinality is greater than the current one, cardinality may be increased.
/// This restriction is created to preserve ordering.
///
/// # Arguments
///
/// * `self` - The observation account to write in
/// * `block_timestamp` - The timestamp of the new observation
/// * `tick` - The active tick at the time of the new observation
/// * `liquidity` - The total in-range liquidity at the time of the new observation
/// * `cardinality` - The number of populated elements in the oracle array
/// * `cardinality_next` - The new length of the oracle array, independent of population
///
pub fn update(
&mut self,
block_timestamp: u32,
tick: i32,
liquidity: u64,
cardinality: u16,
cardinality_next: u16,
) -> u16 {
if self.block_timestamp == block_timestamp {
return cardinality;
}
*self = self.transform(block_timestamp, tick, liquidity);
if cardinality_next > cardinality && self.index == (cardinality - 1) {
cardinality_next
} else {
cardinality
}
}
/// Makes a new observation for the current block timestamp
///
/// # Arguments
///
/// * `last` - The most recently written observation
/// * `time` - The current block timestamp
/// * `liquidity` - The current in-range pool liquidity
///
pub fn observe_latest(self, time: u32, tick: i32, liquidity: u64) -> (i64, u64) {
let mut last = self;
if self.block_timestamp != time {
last = self.transform(time, tick, liquidity)
}
(
last.tick_cumulative,
last.seconds_per_liquidity_cumulative_x32,
)
}
}
/// Returns the block timestamp truncated to 32 bits, i.e. mod 2**32
///
pub fn _block_timestamp() -> u32 {
Clock::get().unwrap().unix_timestamp as u32 // truncation is desired
}
/// Emitted by the pool for increases to the number of observations that can be stored
///
/// `observation_cardinality_next` is not the observation cardinality until an observation
/// is written at the index just before a mint/swap/burn.
///
#[event]
pub struct IncreaseObservationCardinalityNext {
/// The previous value of the next observation cardinality
pub observation_cardinality_next_old: u16,
/// The updated value of the next observation cardinality
pub observation_cardinality_next_new: u16,
}
#[cfg(test)]
mod test {
use super::*;
}