abstract_std/objects/
time_weighted_average.rsuse cosmwasm_std::{Addr, Decimal, Env, QuerierWrapper, Storage, Timestamp, Uint128};
use cw_storage_plus::Item;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::AbstractResult;
pub const DEFAULT_PRECISION: u8 = 6;
pub struct TimeWeightedAverage(Item<TimeWeightedAverageData>);
impl TimeWeightedAverage {
pub const fn new(key: &'static str) -> Self {
Self(Item::new(key))
}
pub fn instantiate(
&self,
store: &mut dyn Storage,
env: &Env,
precision: Option<u8>,
averaging_period: u64,
) -> AbstractResult<()> {
let block_time = env.block.time;
let twa = TimeWeightedAverageData {
cumulative_value: 0,
last_block_time: block_time,
precision: precision.unwrap_or(DEFAULT_PRECISION),
average_value: Decimal::zero(),
averaging_period,
last_averaging_cumulative_value: 0,
last_averaging_block_time: block_time,
last_averaging_block_height: env.block.height,
};
self.0.save(store, &twa).map_err(Into::into)
}
pub fn accumulate(
&self,
env: &Env,
store: &mut dyn Storage,
current_value: Decimal,
) -> AbstractResult<Option<u128>> {
let mut twa = self.0.load(store)?;
let block_time = env.block.time;
if block_time <= twa.last_block_time {
return Ok(None);
}
let time_elapsed = Uint128::from(block_time.seconds() - twa.last_block_time.seconds());
twa.last_block_time = block_time;
if !current_value.is_zero() {
twa.cumulative_value = twa
.cumulative_value
.wrapping_add(time_elapsed.mul_floor(current_value).u128());
};
self.0.save(store, &twa)?;
Ok(Some(twa.cumulative_value))
}
pub fn get_value(&self, store: &dyn Storage) -> AbstractResult<Decimal> {
Ok(self.0.load(store)?.average_value)
}
pub fn load(&self, store: &dyn Storage) -> AbstractResult<TimeWeightedAverageData> {
self.0.load(store).map_err(Into::into)
}
pub fn query(
&self,
querier: &QuerierWrapper,
remote_contract_addr: Addr,
) -> AbstractResult<TimeWeightedAverageData> {
self.0
.query(querier, remote_contract_addr)
.map_err(Into::into)
}
pub fn try_update_value(
&self,
env: &Env,
store: &mut dyn Storage,
) -> AbstractResult<Option<Decimal>> {
let mut twa = self.0.load(store)?;
let block_time = env.block.time;
let time_elapsed = block_time.seconds() - twa.last_averaging_block_time.seconds();
if time_elapsed < twa.averaging_period {
return Ok(None);
}
let new_average_value = Decimal::from_ratio(
twa.cumulative_value
.wrapping_sub(twa.last_averaging_cumulative_value),
time_elapsed,
);
twa = TimeWeightedAverageData {
average_value: new_average_value,
last_averaging_block_time: block_time,
last_averaging_cumulative_value: twa.cumulative_value,
..twa
};
self.0.save(store, &twa)?;
Ok(Some(new_average_value))
}
pub fn update_settings(
&self,
_env: &Env,
store: &mut dyn Storage,
averaging_period: u64,
) -> AbstractResult<()> {
let mut twa = self.0.load(store)?;
twa.averaging_period = averaging_period;
self.0.save(store, &twa).map_err(Into::into)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct TimeWeightedAverageData {
pub precision: u8,
pub last_block_time: Timestamp,
pub cumulative_value: u128,
pub last_averaging_block_time: Timestamp,
pub last_averaging_block_height: u64,
pub last_averaging_cumulative_value: u128,
pub averaging_period: u64,
pub average_value: Decimal,
}
impl TimeWeightedAverageData {
pub fn needs_refresh(&self, env: &Env) -> bool {
let block_time = env.block.time;
let time_elapsed = block_time.seconds() - self.last_averaging_block_time.seconds();
time_elapsed >= self.averaging_period
}
}