Skip to content

Commit 03bb5a2

Browse files
committed
reformulated test cases + added some bounds description
1 parent 017cad1 commit 03bb5a2

File tree

1 file changed

+87
-14
lines changed

1 file changed

+87
-14
lines changed

pyth-sdk/src/price.rs

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,20 @@ impl Price {
108108
/// discount_precision: u64, the precision used for discounts
109109
///
110110
/// Logic
111-
/// collateral_valuation_price = (deposits / max_deposits) * ((discount_precision-discount_final) / discount_precision) * price + ((max_deposits - deposits) / max_deposits) * ((discount_precision - discount_initial) / discount_precision) * price
111+
/// A = deposits / max_deposits
112+
/// B = (discount_precision-discount_final) / discount_precision
113+
/// C = (max_deposits - deposits) / max_deposits
114+
/// D = (discount_precision - discount_initial) / discount_precision
115+
/// collateral_valuation_price = [(A * B) + (C * D)] * price
116+
///
117+
/// Bounds due to precision loss
118+
/// A, B, C, D each has precision up to PD_SCALE
119+
/// x = 1/PD_SCALE
120+
/// Err(A*B) <= (1+x)^2 - 1 (in fractional terms)
121+
/// Err(C*D) <= (1+x)^2 - 1
122+
/// Err(A*B + C*D) <= 2*(1+x)^2 - 2
123+
/// Err((A*B + C*D) * price) <= 2*(1+x)^2 - 2 = 2x^2 + 2x ~= 2/PD_SCALE
124+
/// Thus, we expect the computed collateral valuation price to be no more than 2/PD_SCALE off of the mathematically true value
112125
pub fn get_collateral_valuation_price(&self, deposits: u64, max_deposits: u64, discount_initial: u64, discount_final: u64, discount_precision: u64) -> Result<Price, LiquidityOracleError> {
113126
if max_deposits < deposits {
114127
return Err(LiquidityOracleError::ExceedsMaxDeposits.into());
@@ -172,18 +185,9 @@ impl Price {
172185
let initial_percentage = diff_discount_precision_initial_as_price.div(&discount_precision_as_price).ok_or(LiquidityOracleError::NoneEncountered)?;
173186
let final_percentage = diff_discount_precision_final_as_price.div(&discount_precision_as_price).ok_or(LiquidityOracleError::NoneEncountered)?;
174187

175-
176-
// compute left and right terms of the sum
177-
let mut left = self.mul(&deposits_percentage).
178-
ok_or(LiquidityOracleError::NoneEncountered)?.
179-
mul(&final_percentage).
180-
ok_or(LiquidityOracleError::NoneEncountered)?
181-
;
182-
let mut right = self.mul(&remaining_depositable_percentage).
183-
ok_or(LiquidityOracleError::NoneEncountered)?.
184-
mul(&initial_percentage).
185-
ok_or(LiquidityOracleError::NoneEncountered)?
186-
;
188+
// // compute left and right terms of the sum
189+
let mut left = deposits_percentage.mul(&final_percentage).ok_or(LiquidityOracleError::NoneEncountered)?;
190+
let mut right = remaining_depositable_percentage.mul(&initial_percentage).ok_or(LiquidityOracleError::NoneEncountered)?;
187191

188192
// scale left and right to match expo; need to ensure no overflow so have a match for NoneEncountered error
189193
if left.expo > right.expo {
@@ -209,7 +213,11 @@ impl Price {
209213
}
210214
}
211215

212-
let price_discounted = left.add(&right).ok_or(LiquidityOracleError::NoneEncountered)?;
216+
// get product term
217+
let mult_discounted = left.add(&right).ok_or(LiquidityOracleError::NoneEncountered)?;
218+
219+
// get price discounted
220+
let price_discounted = self.mul(&mult_discounted).ok_or(LiquidityOracleError::NoneEncountered)?;
213221

214222
// we want to scale the original confidence to the new expo
215223
let conf_scaled = self.scale_confidence_to_exponent(price_discounted.expo);
@@ -1167,6 +1175,71 @@ mod test {
11671175
pc(90 * (PD_SCALE as i64), 2 * PD_SCALE, 0)
11681176
);
11691177

1178+
// test precision limits
1179+
succeeds(
1180+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1181+
1,
1182+
1_000_000_000_000_000_000,
1183+
0,
1184+
10,
1185+
100,
1186+
pc(100 * (PD_SCALE as i64)-1000, 2 * PD_SCALE, 0),
1187+
);
1188+
succeeds(
1189+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1190+
100_000_000,
1191+
1_000_000_000_000_000_000,
1192+
0,
1193+
10,
1194+
100,
1195+
pc(100 * (PD_SCALE as i64)-1000, 2 * PD_SCALE, 0),
1196+
);
1197+
succeeds(
1198+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1199+
1_000_000_000,
1200+
1_000_000_000_000_000_000,
1201+
0,
1202+
10,
1203+
100,
1204+
pc(100 * (PD_SCALE as i64)-1000, 2 * PD_SCALE, 0),
1205+
);
1206+
succeeds(
1207+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1208+
10_000_000_000,
1209+
1_000_000_000_000_000_000,
1210+
0,
1211+
10,
1212+
100,
1213+
pc(100 * (PD_SCALE as i64)-1000, 2 * PD_SCALE, 0),
1214+
);
1215+
succeeds(
1216+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1217+
100_000_000_000,
1218+
1_000_000_000_000_000_000,
1219+
0,
1220+
10,
1221+
100,
1222+
pc(100 * (PD_SCALE as i64)-1000, 2 * PD_SCALE, 0),
1223+
);
1224+
succeeds(
1225+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1226+
200_000_000_000,
1227+
1_000_000_000_000_000_000,
1228+
0,
1229+
10,
1230+
100,
1231+
pc(100 * (PD_SCALE as i64)-2000, 2 * PD_SCALE, 0),
1232+
);
1233+
succeeds(
1234+
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),
1235+
1_000_000_000_000,
1236+
1_000_000_000_000_000_000,
1237+
0,
1238+
10,
1239+
100,
1240+
pc(100 * (PD_SCALE as i64)-10000, 2 * PD_SCALE, 0),
1241+
);
1242+
11701243
// fails bc over max deposit limit
11711244
fails_exceeds_max_deposits(
11721245
pc(100 * (PD_SCALE as i64), 2 * PD_SCALE, 0),

0 commit comments

Comments
 (0)