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
use crate::AbstractResult;
use cosmwasm_std::{Decimal, Env, Storage, Uint128};
use cw_storage_plus::Item;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::ops::Mul;
pub const DEFAULT_PRECISION: u8 = 6;
pub struct TimeWeightedAverage<'a>(Item<'a, TimeWeightedAverageData>);
impl<'a> TimeWeightedAverage<'a> {
pub const fn new(key: &'a 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.seconds();
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, u64)>> {
let mut twa = self.0.load(store)?;
let block_time = env.block.time.seconds();
if block_time <= twa.last_block_time {
return Ok(None);
}
let time_elapsed = Uint128::from(block_time - twa.last_block_time);
twa.last_block_time = block_time;
if !current_value.is_zero() {
twa.cumulative_value = twa
.cumulative_value
.wrapping_add(time_elapsed.mul(current_value).u128());
};
self.0.save(store, &twa)?;
Ok(Some((twa.cumulative_value, block_time)))
}
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 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.seconds();
let time_elapsed = block_time - twa.last_averaging_block_time;
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: u64,
pub cumulative_value: u128,
pub last_averaging_block_time: u64,
pub last_averaging_block_height: u64,
pub last_averaging_cumulative_value: u128,
pub averaging_period: u64,
average_value: Decimal,
}