abstract_core/objects/
time_weighted_average.rs1use std::ops::Mul;
10
11use cosmwasm_std::{Addr, Decimal, Env, QuerierWrapper, Storage, Timestamp, Uint128};
12use cw_storage_plus::Item;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15
16use crate::AbstractResult;
17
18pub const DEFAULT_PRECISION: u8 = 6;
19
20pub struct TimeWeightedAverage<'a>(Item<'a, TimeWeightedAverageData>);
22
23impl<'a> TimeWeightedAverage<'a> {
24 pub const fn new(key: &'a str) -> Self {
25 Self(Item::new(key))
26 }
27 pub fn instantiate(
28 &self,
29 store: &mut dyn Storage,
30 env: &Env,
31 precision: Option<u8>,
32 averaging_period: u64,
33 ) -> AbstractResult<()> {
34 let block_time = env.block.time;
35
36 let twa = TimeWeightedAverageData {
37 cumulative_value: 0,
38 last_block_time: block_time,
39 precision: precision.unwrap_or(DEFAULT_PRECISION),
40 average_value: Decimal::zero(),
41 averaging_period,
42 last_averaging_cumulative_value: 0,
43 last_averaging_block_time: block_time,
44 last_averaging_block_height: env.block.height,
45 };
46 self.0.save(store, &twa).map_err(Into::into)
47 }
48
49 pub fn accumulate(
52 &self,
53 env: &Env,
54 store: &mut dyn Storage,
55 current_value: Decimal,
56 ) -> AbstractResult<Option<u128>> {
57 let mut twa = self.0.load(store)?;
58 let block_time = env.block.time;
59 if block_time <= twa.last_block_time {
60 return Ok(None);
61 }
62
63 let time_elapsed = Uint128::from(block_time.seconds() - twa.last_block_time.seconds());
64 twa.last_block_time = block_time;
65
66 if !current_value.is_zero() {
67 twa.cumulative_value = twa
68 .cumulative_value
69 .wrapping_add(time_elapsed.mul(current_value).u128());
70 };
71 self.0.save(store, &twa)?;
72 Ok(Some(twa.cumulative_value))
73 }
74
75 pub fn get_value(&self, store: &dyn Storage) -> AbstractResult<Decimal> {
76 Ok(self.0.load(store)?.average_value)
77 }
78
79 pub fn load(&self, store: &dyn Storage) -> AbstractResult<TimeWeightedAverageData> {
80 self.0.load(store).map_err(Into::into)
81 }
82
83 pub fn query(
84 &self,
85 querier: &QuerierWrapper,
86 remote_contract_addr: Addr,
87 ) -> AbstractResult<TimeWeightedAverageData> {
88 self.0
89 .query(querier, remote_contract_addr)
90 .map_err(Into::into)
91 }
92
93 pub fn try_update_value(
95 &self,
96 env: &Env,
97 store: &mut dyn Storage,
98 ) -> AbstractResult<Option<Decimal>> {
99 let mut twa = self.0.load(store)?;
100
101 let block_time = env.block.time;
102
103 let time_elapsed = block_time.seconds() - twa.last_averaging_block_time.seconds();
104
105 if time_elapsed < twa.averaging_period {
107 return Ok(None);
108 }
109
110 let new_average_value = Decimal::from_ratio(
112 twa.cumulative_value
113 .wrapping_sub(twa.last_averaging_cumulative_value),
114 time_elapsed,
115 );
116
117 twa = TimeWeightedAverageData {
118 average_value: new_average_value,
119 last_averaging_block_time: block_time,
120 last_averaging_cumulative_value: twa.cumulative_value,
121 ..twa
122 };
123 self.0.save(store, &twa)?;
124 Ok(Some(new_average_value))
125 }
126
127 pub fn update_settings(
128 &self,
129 _env: &Env,
130 store: &mut dyn Storage,
131 averaging_period: u64,
132 ) -> AbstractResult<()> {
133 let mut twa = self.0.load(store)?;
134 twa.averaging_period = averaging_period;
135 self.0.save(store, &twa).map_err(Into::into)
136 }
137}
138
139#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
140pub struct TimeWeightedAverageData {
141 pub precision: u8,
143 pub last_block_time: Timestamp,
144 pub cumulative_value: u128,
145
146 pub last_averaging_block_time: Timestamp,
148 pub last_averaging_block_height: u64,
149 pub last_averaging_cumulative_value: u128,
150 pub averaging_period: u64,
151 pub average_value: Decimal,
153}
154
155impl TimeWeightedAverageData {
156 pub fn needs_refresh(&self, env: &Env) -> bool {
157 let block_time = env.block.time;
158
159 let time_elapsed = block_time.seconds() - self.last_averaging_block_time.seconds();
160
161 time_elapsed >= self.averaging_period
163 }
164}