@paraswap/dex-lib 4.8.36 → 4.8.37-native-dex-math.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/dex/aave-gsm/config.js +2 -2
- package/build/dex/idex.d.ts +1 -1
- package/build/dex/pancakeswap-v3/pancakeswap-v3-pool.d.ts +2 -0
- package/build/dex/pancakeswap-v3/pancakeswap-v3-pool.js +5 -0
- package/build/dex/pancakeswap-v3/pancakeswap-v3-pool.js.map +1 -1
- package/build/dex/pancakeswap-v3/pancakeswap-v3.d.ts +6 -3
- package/build/dex/pancakeswap-v3/pancakeswap-v3.js +85 -9
- package/build/dex/pancakeswap-v3/pancakeswap-v3.js.map +1 -1
- package/build/dex/solidly-v3/solidly-v3-pool.d.ts +3 -0
- package/build/dex/solidly-v3/solidly-v3-pool.js +8 -0
- package/build/dex/solidly-v3/solidly-v3-pool.js.map +1 -1
- package/build/dex/solidly-v3/solidly-v3.d.ts +5 -2
- package/build/dex/solidly-v3/solidly-v3.js +84 -8
- package/build/dex/solidly-v3/solidly-v3.js.map +1 -1
- package/build/dex/uniswap-v3/scripts/measure-calc-time.js +222 -110
- package/build/dex/uniswap-v3/scripts/measure-calc-time.js.map +1 -1
- package/build/dex/uniswap-v3/types.d.ts +1 -0
- package/build/dex/uniswap-v3/types.js.map +1 -1
- package/build/dex/uniswap-v3/uniswap-v3-pool.d.ts +2 -0
- package/build/dex/uniswap-v3/uniswap-v3-pool.js +5 -0
- package/build/dex/uniswap-v3/uniswap-v3-pool.js.map +1 -1
- package/build/dex/uniswap-v3/uniswap-v3.d.ts +11 -3
- package/build/dex/uniswap-v3/uniswap-v3.js +86 -9
- package/build/dex/uniswap-v3/uniswap-v3.js.map +1 -1
- package/build/dex/uniswap-v4/types.d.ts +1 -0
- package/build/dex/uniswap-v4/uniswap-v4-pool-manager.d.ts +2 -0
- package/build/dex/uniswap-v4/uniswap-v4-pool-manager.js +3 -0
- package/build/dex/uniswap-v4/uniswap-v4-pool-manager.js.map +1 -1
- package/build/dex/uniswap-v4/uniswap-v4-pool.d.ts +3 -0
- package/build/dex/uniswap-v4/uniswap-v4-pool.js +18 -0
- package/build/dex/uniswap-v4/uniswap-v4-pool.js.map +1 -1
- package/build/dex/uniswap-v4/uniswap-v4.d.ts +3 -1
- package/build/dex/uniswap-v4/uniswap-v4.js +74 -14
- package/build/dex/uniswap-v4/uniswap-v4.js.map +1 -1
- package/build/pricing-helper.d.ts +1 -1
- package/build/pricing-helper.js +2 -2
- package/build/pricing-helper.js.map +1 -1
- package/native/Cargo.lock +331 -0
- package/native/Cargo.toml +22 -0
- package/native/build.rs +5 -0
- package/native/package-lock.json +32 -0
- package/native/package.json +20 -0
- package/native/src/config.rs +73 -0
- package/native/src/lib.rs +528 -0
- package/native/src/math/bit_math.rs +177 -0
- package/native/src/math/full_math.rs +217 -0
- package/native/src/math/liquidity_math.rs +72 -0
- package/native/src/math/mod.rs +10 -0
- package/native/src/math/oracle.rs +493 -0
- package/native/src/math/sqrt_price_math.rs +272 -0
- package/native/src/math/swap_math.rs +306 -0
- package/native/src/math/tick.rs +239 -0
- package/native/src/math/tick_bitmap.rs +312 -0
- package/native/src/math/tick_math.rs +321 -0
- package/native/src/math/unsafe_math.rs +67 -0
- package/native/src/pool_state.rs +41 -0
- package/native/src/query_outputs.rs +379 -0
- package/native/src/v4_query_outputs.rs +255 -0
- package/package.json +1 -1
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
use ethnum::{I256, U256};
|
|
2
|
+
use std::collections::HashMap;
|
|
3
|
+
|
|
4
|
+
// 2^160 - 1: hi_128 = 0xFFFFFFFF, lo_128 = u128::MAX
|
|
5
|
+
const MASK_160: U256 = U256::from_words(0xFFFF_FFFF, u128::MAX);
|
|
6
|
+
|
|
7
|
+
/// An oracle observation.
|
|
8
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
9
|
+
pub struct OracleObservation {
|
|
10
|
+
pub block_timestamp: U256,
|
|
11
|
+
pub tick_cumulative: I256,
|
|
12
|
+
pub seconds_per_liquidity_cumulative_x128: U256,
|
|
13
|
+
pub initialized: bool,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl Default for OracleObservation {
|
|
17
|
+
fn default() -> Self {
|
|
18
|
+
OracleObservation {
|
|
19
|
+
block_timestamp: U256::ZERO,
|
|
20
|
+
tick_cumulative: I256::ZERO,
|
|
21
|
+
seconds_per_liquidity_cumulative_x128: U256::ZERO,
|
|
22
|
+
initialized: false,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Observation candidate pair used in binary search.
|
|
28
|
+
pub struct OracleObservationCandidates {
|
|
29
|
+
pub before_or_at: OracleObservation,
|
|
30
|
+
pub at_or_after: OracleObservation,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Transforms a previous observation into a new one, given the time elapsed and the
|
|
34
|
+
/// current tick and liquidity values.
|
|
35
|
+
///
|
|
36
|
+
/// The `block_timestamp` parameter corresponds to `state.blockTimestamp` in the TS version.
|
|
37
|
+
/// In the original TS, `transform` receives `state` and uses `state.blockTimestamp` for the
|
|
38
|
+
/// output's blockTimestamp. Here we pass it explicitly as `block_timestamp_state`.
|
|
39
|
+
pub fn transform(
|
|
40
|
+
last: &OracleObservation,
|
|
41
|
+
block_timestamp: U256,
|
|
42
|
+
block_timestamp_state: U256,
|
|
43
|
+
tick: I256,
|
|
44
|
+
liquidity: U256,
|
|
45
|
+
) -> OracleObservation {
|
|
46
|
+
let delta = block_timestamp - last.block_timestamp;
|
|
47
|
+
|
|
48
|
+
// tickCumulative: last.tickCumulative + BigInt.asIntN(56, tick) * delta
|
|
49
|
+
let tick_i56 = sign_extend_i56(tick);
|
|
50
|
+
let delta_signed = delta.as_i256();
|
|
51
|
+
let tick_cumulative = last.tick_cumulative + tick_i56 * delta_signed;
|
|
52
|
+
|
|
53
|
+
// secondsPerLiquidityCumulativeX128:
|
|
54
|
+
// last.spl + (BigInt.asUintN(160, delta) << 128) / (liquidity > 0 ? liquidity : 1)
|
|
55
|
+
let delta_u160 = delta & MASK_160;
|
|
56
|
+
let numerator = delta_u160 << 128;
|
|
57
|
+
let denominator = if liquidity > U256::ZERO {
|
|
58
|
+
liquidity
|
|
59
|
+
} else {
|
|
60
|
+
U256::ONE
|
|
61
|
+
};
|
|
62
|
+
let seconds_per_liquidity_cumulative_x128 =
|
|
63
|
+
last.seconds_per_liquidity_cumulative_x128 + numerator / denominator;
|
|
64
|
+
|
|
65
|
+
OracleObservation {
|
|
66
|
+
block_timestamp: block_timestamp_state,
|
|
67
|
+
tick_cumulative,
|
|
68
|
+
seconds_per_liquidity_cumulative_x128,
|
|
69
|
+
initialized: true,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Writes an oracle observation to the array, returning the updated index and cardinality.
|
|
74
|
+
///
|
|
75
|
+
/// `block_timestamp_state` is the state's block timestamp (used for comparison and as the
|
|
76
|
+
/// new observation's timestamp).
|
|
77
|
+
///
|
|
78
|
+
/// Returns `(updated_index, updated_cardinality)`.
|
|
79
|
+
pub fn write(
|
|
80
|
+
observations: &mut HashMap<u16, OracleObservation>,
|
|
81
|
+
index: u16,
|
|
82
|
+
block_timestamp: U256,
|
|
83
|
+
block_timestamp_state: U256,
|
|
84
|
+
tick: I256,
|
|
85
|
+
liquidity: U256,
|
|
86
|
+
cardinality: u16,
|
|
87
|
+
cardinality_next: u16,
|
|
88
|
+
) -> (u16, u16) {
|
|
89
|
+
let last = observations
|
|
90
|
+
.get(&index)
|
|
91
|
+
.copied()
|
|
92
|
+
.expect("last observation must exist");
|
|
93
|
+
|
|
94
|
+
// If the block timestamp hasn't changed, no update needed
|
|
95
|
+
if last.block_timestamp == block_timestamp_state {
|
|
96
|
+
return (index, cardinality);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let cardinality_updated = if cardinality_next > cardinality && index == cardinality - 1 {
|
|
100
|
+
cardinality_next
|
|
101
|
+
} else {
|
|
102
|
+
cardinality
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let index_updated = ((index as u32 + 1) % cardinality_updated as u32) as u16;
|
|
106
|
+
|
|
107
|
+
let new_observation = transform(&last, block_timestamp, block_timestamp_state, tick, liquidity);
|
|
108
|
+
observations.insert(index_updated, new_observation);
|
|
109
|
+
|
|
110
|
+
// In the TS code, if indexUpdated !== index, the old index is deleted
|
|
111
|
+
if index_updated != index {
|
|
112
|
+
observations.remove(&index);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
(index_updated, cardinality_updated)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Compares two timestamps with overflow-aware less-than-or-equal.
|
|
119
|
+
///
|
|
120
|
+
/// This handles the uint32 overflow case: if both timestamps are <= time, compare normally.
|
|
121
|
+
/// Otherwise, adjust the one that hasn't overflowed by adding 2^32.
|
|
122
|
+
pub fn lte(time: U256, a: U256, b: U256) -> bool {
|
|
123
|
+
if a <= time && b <= time {
|
|
124
|
+
return a <= b;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let two_pow_32 = U256::ONE << 32;
|
|
128
|
+
let a_adjusted = if a > time { a } else { a + two_pow_32 };
|
|
129
|
+
let b_adjusted = if b > time { b } else { b + two_pow_32 };
|
|
130
|
+
a_adjusted <= b_adjusted
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Binary search for the observations surrounding a target timestamp.
|
|
134
|
+
pub fn binary_search(
|
|
135
|
+
observations: &HashMap<u16, OracleObservation>,
|
|
136
|
+
time: U256,
|
|
137
|
+
target: U256,
|
|
138
|
+
index: u16,
|
|
139
|
+
cardinality: u16,
|
|
140
|
+
) -> OracleObservationCandidates {
|
|
141
|
+
let mut l = ((index as u32 + 1) % cardinality as u32) as u32;
|
|
142
|
+
let mut r = l + cardinality as u32 - 1;
|
|
143
|
+
|
|
144
|
+
loop {
|
|
145
|
+
let i = (l + r) / 2;
|
|
146
|
+
let idx = (i % cardinality as u32) as u16;
|
|
147
|
+
let before_or_at = observations
|
|
148
|
+
.get(&idx)
|
|
149
|
+
.copied()
|
|
150
|
+
.unwrap_or_default();
|
|
151
|
+
|
|
152
|
+
if !before_or_at.initialized {
|
|
153
|
+
l = i + 1;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let after_idx = ((i + 1) % cardinality as u32) as u16;
|
|
158
|
+
let at_or_after = observations
|
|
159
|
+
.get(&after_idx)
|
|
160
|
+
.copied()
|
|
161
|
+
.unwrap_or_default();
|
|
162
|
+
|
|
163
|
+
let target_at_or_after = lte(time, before_or_at.block_timestamp, target);
|
|
164
|
+
|
|
165
|
+
if target_at_or_after && lte(time, target, at_or_after.block_timestamp) {
|
|
166
|
+
return OracleObservationCandidates {
|
|
167
|
+
before_or_at,
|
|
168
|
+
at_or_after,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if !target_at_or_after {
|
|
173
|
+
r = i - 1;
|
|
174
|
+
} else {
|
|
175
|
+
l = i + 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Returns the observations surrounding a target timestamp.
|
|
181
|
+
///
|
|
182
|
+
/// `block_timestamp_state` is the state's block timestamp used for transform.
|
|
183
|
+
pub fn get_surrounding_observations(
|
|
184
|
+
observations: &HashMap<u16, OracleObservation>,
|
|
185
|
+
time: U256,
|
|
186
|
+
target: U256,
|
|
187
|
+
block_timestamp_state: U256,
|
|
188
|
+
tick: I256,
|
|
189
|
+
index: u16,
|
|
190
|
+
liquidity: U256,
|
|
191
|
+
cardinality: u16,
|
|
192
|
+
) -> OracleObservationCandidates {
|
|
193
|
+
let before_or_at = observations
|
|
194
|
+
.get(&index)
|
|
195
|
+
.copied()
|
|
196
|
+
.unwrap_or_default();
|
|
197
|
+
|
|
198
|
+
if lte(time, before_or_at.block_timestamp, target) {
|
|
199
|
+
if before_or_at.block_timestamp == target {
|
|
200
|
+
return OracleObservationCandidates {
|
|
201
|
+
before_or_at,
|
|
202
|
+
at_or_after: before_or_at,
|
|
203
|
+
};
|
|
204
|
+
} else {
|
|
205
|
+
let at_or_after = transform(
|
|
206
|
+
&before_or_at,
|
|
207
|
+
target,
|
|
208
|
+
block_timestamp_state,
|
|
209
|
+
tick,
|
|
210
|
+
liquidity,
|
|
211
|
+
);
|
|
212
|
+
return OracleObservationCandidates {
|
|
213
|
+
before_or_at,
|
|
214
|
+
at_or_after,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let oldest_idx = ((index as u32 + 1) % cardinality as u32) as u16;
|
|
220
|
+
let mut before_or_at = observations
|
|
221
|
+
.get(&oldest_idx)
|
|
222
|
+
.copied()
|
|
223
|
+
.unwrap_or_default();
|
|
224
|
+
|
|
225
|
+
if !before_or_at.initialized {
|
|
226
|
+
before_or_at = observations.get(&0u16).copied().unwrap_or_default();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
assert!(
|
|
230
|
+
lte(time, before_or_at.block_timestamp, target),
|
|
231
|
+
"OLD"
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
binary_search(observations, time, target, index, cardinality)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// Returns the accumulator values as of `secondsAgo` seconds ago from the given time.
|
|
238
|
+
///
|
|
239
|
+
/// `block_timestamp_state` is the state's block timestamp.
|
|
240
|
+
///
|
|
241
|
+
/// Returns `(tick_cumulative, seconds_per_liquidity_cumulative_x128)`.
|
|
242
|
+
pub fn observe_single(
|
|
243
|
+
observations: &HashMap<u16, OracleObservation>,
|
|
244
|
+
time: U256,
|
|
245
|
+
seconds_ago: U256,
|
|
246
|
+
block_timestamp_state: U256,
|
|
247
|
+
tick: I256,
|
|
248
|
+
index: u16,
|
|
249
|
+
liquidity: U256,
|
|
250
|
+
cardinality: u16,
|
|
251
|
+
) -> (I256, U256) {
|
|
252
|
+
if seconds_ago == U256::ZERO {
|
|
253
|
+
let mut last = observations
|
|
254
|
+
.get(&index)
|
|
255
|
+
.copied()
|
|
256
|
+
.unwrap_or_default();
|
|
257
|
+
if last.block_timestamp != time {
|
|
258
|
+
last = transform(&last, time, block_timestamp_state, tick, liquidity);
|
|
259
|
+
}
|
|
260
|
+
return (
|
|
261
|
+
last.tick_cumulative,
|
|
262
|
+
last.seconds_per_liquidity_cumulative_x128,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let target = time - seconds_ago;
|
|
267
|
+
|
|
268
|
+
let OracleObservationCandidates {
|
|
269
|
+
before_or_at,
|
|
270
|
+
at_or_after,
|
|
271
|
+
} = get_surrounding_observations(
|
|
272
|
+
observations,
|
|
273
|
+
time,
|
|
274
|
+
target,
|
|
275
|
+
block_timestamp_state,
|
|
276
|
+
tick,
|
|
277
|
+
index,
|
|
278
|
+
liquidity,
|
|
279
|
+
cardinality,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
if target == before_or_at.block_timestamp {
|
|
283
|
+
return (
|
|
284
|
+
before_or_at.tick_cumulative,
|
|
285
|
+
before_or_at.seconds_per_liquidity_cumulative_x128,
|
|
286
|
+
);
|
|
287
|
+
} else if target == at_or_after.block_timestamp {
|
|
288
|
+
return (
|
|
289
|
+
at_or_after.tick_cumulative,
|
|
290
|
+
at_or_after.seconds_per_liquidity_cumulative_x128,
|
|
291
|
+
);
|
|
292
|
+
} else {
|
|
293
|
+
let observation_time_delta =
|
|
294
|
+
at_or_after.block_timestamp - before_or_at.block_timestamp;
|
|
295
|
+
let target_delta = target - before_or_at.block_timestamp;
|
|
296
|
+
let observation_time_delta_signed = observation_time_delta.as_i256();
|
|
297
|
+
let target_delta_signed = target_delta.as_i256();
|
|
298
|
+
|
|
299
|
+
let tick_cumulative = before_or_at.tick_cumulative
|
|
300
|
+
+ ((at_or_after.tick_cumulative - before_or_at.tick_cumulative)
|
|
301
|
+
/ observation_time_delta_signed)
|
|
302
|
+
* target_delta_signed;
|
|
303
|
+
|
|
304
|
+
// secondsPerLiquidityCumulativeX128 interpolation:
|
|
305
|
+
// beforeOrAt.spl + BigInt.asUintN(160,
|
|
306
|
+
// (BigInt.asUintN(256, atOrAfter.spl - beforeOrAt.spl) * targetDelta) / observationTimeDelta
|
|
307
|
+
// )
|
|
308
|
+
let spl_diff = at_or_after.seconds_per_liquidity_cumulative_x128
|
|
309
|
+
.wrapping_sub(before_or_at.seconds_per_liquidity_cumulative_x128);
|
|
310
|
+
let spl_interpolated = (spl_diff * target_delta) / observation_time_delta;
|
|
311
|
+
let spl_masked = spl_interpolated & MASK_160;
|
|
312
|
+
let seconds_per_liquidity_cumulative_x128 =
|
|
313
|
+
before_or_at.seconds_per_liquidity_cumulative_x128 + spl_masked;
|
|
314
|
+
|
|
315
|
+
return (tick_cumulative, seconds_per_liquidity_cumulative_x128);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/// Sign-extend a value to signed 56-bit (equivalent to BigInt.asIntN(56, x)).
|
|
320
|
+
fn sign_extend_i56(val: I256) -> I256 {
|
|
321
|
+
let mask: I256 = (I256::ONE << 56) - I256::ONE;
|
|
322
|
+
let masked = val & mask;
|
|
323
|
+
if masked & (I256::ONE << 55) != I256::ZERO {
|
|
324
|
+
masked | !mask
|
|
325
|
+
} else {
|
|
326
|
+
masked
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
#[cfg(test)]
|
|
331
|
+
mod tests {
|
|
332
|
+
use super::*;
|
|
333
|
+
|
|
334
|
+
fn make_obs(
|
|
335
|
+
block_timestamp: u64,
|
|
336
|
+
tick_cumulative: i64,
|
|
337
|
+
spl: u64,
|
|
338
|
+
initialized: bool,
|
|
339
|
+
) -> OracleObservation {
|
|
340
|
+
OracleObservation {
|
|
341
|
+
block_timestamp: U256::from(block_timestamp),
|
|
342
|
+
tick_cumulative: I256::from(tick_cumulative),
|
|
343
|
+
seconds_per_liquidity_cumulative_x128: U256::from(spl),
|
|
344
|
+
initialized,
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#[test]
|
|
349
|
+
fn test_lte_both_below_time() {
|
|
350
|
+
assert!(lte(U256::from(100u64), U256::from(50u64), U256::from(60u64)));
|
|
351
|
+
assert!(!lte(U256::from(100u64), U256::from(60u64), U256::from(50u64)));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#[test]
|
|
355
|
+
fn test_lte_equal() {
|
|
356
|
+
assert!(lte(U256::from(100u64), U256::from(50u64), U256::from(50u64)));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#[test]
|
|
360
|
+
fn test_lte_overflow_case() {
|
|
361
|
+
// a is past overflow, b is before overflow
|
|
362
|
+
// time = 10, a = 5 (not overflowed), b = 15 (overflowed past time)
|
|
363
|
+
// a <= time? yes. b <= time? no.
|
|
364
|
+
// a_adjusted = 5 + 2^32, b_adjusted = 15
|
|
365
|
+
// 5 + 2^32 > 15, so a is NOT <= b
|
|
366
|
+
assert!(!lte(U256::from(10u64), U256::from(5u64), U256::from(15u64)));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#[test]
|
|
370
|
+
fn test_transform_basic() {
|
|
371
|
+
let last = make_obs(100, 5000, 10000, true);
|
|
372
|
+
let result = transform(
|
|
373
|
+
&last,
|
|
374
|
+
U256::from(110u64), // blockTimestamp
|
|
375
|
+
U256::from(110u64), // state.blockTimestamp
|
|
376
|
+
I256::from(50i64), // tick
|
|
377
|
+
U256::from(1000u64), // liquidity
|
|
378
|
+
);
|
|
379
|
+
assert_eq!(result.block_timestamp, U256::from(110u64));
|
|
380
|
+
// tickCumulative = 5000 + 50 * 10 = 5500
|
|
381
|
+
assert_eq!(result.tick_cumulative, I256::from(5500i64));
|
|
382
|
+
assert!(result.initialized);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
#[test]
|
|
386
|
+
fn test_transform_zero_liquidity() {
|
|
387
|
+
let last = make_obs(100, 0, 0, true);
|
|
388
|
+
let result = transform(
|
|
389
|
+
&last,
|
|
390
|
+
U256::from(110u64),
|
|
391
|
+
U256::from(110u64),
|
|
392
|
+
I256::from(10i64),
|
|
393
|
+
U256::ZERO, // zero liquidity => denominator becomes 1
|
|
394
|
+
);
|
|
395
|
+
// tickCumulative = 0 + 10 * 10 = 100
|
|
396
|
+
assert_eq!(result.tick_cumulative, I256::from(100i64));
|
|
397
|
+
// secondsPerLiquidity = 0 + (10 << 128) / 1 = 10 << 128
|
|
398
|
+
let expected_spl = U256::from(10u64) << 128;
|
|
399
|
+
assert_eq!(result.seconds_per_liquidity_cumulative_x128, expected_spl);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#[test]
|
|
403
|
+
fn test_write_same_timestamp_noop() {
|
|
404
|
+
let mut observations = HashMap::new();
|
|
405
|
+
observations.insert(0u16, make_obs(100, 0, 0, true));
|
|
406
|
+
|
|
407
|
+
let (idx, card) = write(
|
|
408
|
+
&mut observations,
|
|
409
|
+
0, // index
|
|
410
|
+
U256::from(100u64), // blockTimestamp
|
|
411
|
+
U256::from(100u64), // state.blockTimestamp (same)
|
|
412
|
+
I256::from(10i64),
|
|
413
|
+
U256::from(1000u64),
|
|
414
|
+
1, // cardinality
|
|
415
|
+
1, // cardinalityNext
|
|
416
|
+
);
|
|
417
|
+
assert_eq!(idx, 0);
|
|
418
|
+
assert_eq!(card, 1);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#[test]
|
|
422
|
+
fn test_write_new_observation() {
|
|
423
|
+
let mut observations = HashMap::new();
|
|
424
|
+
observations.insert(0u16, make_obs(100, 0, 0, true));
|
|
425
|
+
|
|
426
|
+
let (idx, card) = write(
|
|
427
|
+
&mut observations,
|
|
428
|
+
0,
|
|
429
|
+
U256::from(110u64),
|
|
430
|
+
U256::from(110u64),
|
|
431
|
+
I256::from(10i64),
|
|
432
|
+
U256::from(1000u64),
|
|
433
|
+
1,
|
|
434
|
+
2,
|
|
435
|
+
);
|
|
436
|
+
// cardinality should increase because cardinalityNext > cardinality and index == cardinality - 1
|
|
437
|
+
assert_eq!(card, 2);
|
|
438
|
+
assert_eq!(idx, 1);
|
|
439
|
+
assert!(observations.get(&1u16).unwrap().initialized);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
#[test]
|
|
443
|
+
fn test_observe_single_zero_seconds_ago() {
|
|
444
|
+
let mut observations = HashMap::new();
|
|
445
|
+
observations.insert(0u16, make_obs(100, 5000, 10000, true));
|
|
446
|
+
|
|
447
|
+
let (tick_cum, spl) = observe_single(
|
|
448
|
+
&observations,
|
|
449
|
+
U256::from(100u64),
|
|
450
|
+
U256::ZERO,
|
|
451
|
+
U256::from(100u64),
|
|
452
|
+
I256::from(50i64),
|
|
453
|
+
0,
|
|
454
|
+
U256::from(1000u64),
|
|
455
|
+
1,
|
|
456
|
+
);
|
|
457
|
+
// secondsAgo == 0 and last.blockTimestamp == time, so return last values directly
|
|
458
|
+
assert_eq!(tick_cum, I256::from(5000i64));
|
|
459
|
+
assert_eq!(spl, U256::from(10000u64));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
#[test]
|
|
463
|
+
fn test_observe_single_transforms_when_timestamp_differs() {
|
|
464
|
+
let mut observations = HashMap::new();
|
|
465
|
+
observations.insert(0u16, make_obs(100, 5000, 10000, true));
|
|
466
|
+
|
|
467
|
+
let (tick_cum, _spl) = observe_single(
|
|
468
|
+
&observations,
|
|
469
|
+
U256::from(110u64), // current time
|
|
470
|
+
U256::ZERO, // secondsAgo = 0
|
|
471
|
+
U256::from(110u64),
|
|
472
|
+
I256::from(50i64), // current tick
|
|
473
|
+
0,
|
|
474
|
+
U256::from(1000u64),
|
|
475
|
+
1,
|
|
476
|
+
);
|
|
477
|
+
// Should transform: tickCumulative = 5000 + 50 * 10 = 5500
|
|
478
|
+
assert_eq!(tick_cum, I256::from(5500i64));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
#[test]
|
|
482
|
+
fn test_sign_extend_i56_positive() {
|
|
483
|
+
let val = I256::from(42i64);
|
|
484
|
+
assert_eq!(sign_extend_i56(val), I256::from(42i64));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
#[test]
|
|
488
|
+
fn test_sign_extend_i56_negative() {
|
|
489
|
+
let val = I256::from(-1i64);
|
|
490
|
+
let result = sign_extend_i56(val);
|
|
491
|
+
assert_eq!(result, I256::from(-1i64));
|
|
492
|
+
}
|
|
493
|
+
}
|