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