@@ -108,7 +108,20 @@ impl Price {
108
108
/// discount_precision: u64, the precision used for discounts
109
109
///
110
110
/// 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
112
125
pub fn get_collateral_valuation_price ( & self , deposits : u64 , max_deposits : u64 , discount_initial : u64 , discount_final : u64 , discount_precision : u64 ) -> Result < Price , LiquidityOracleError > {
113
126
if max_deposits < deposits {
114
127
return Err ( LiquidityOracleError :: ExceedsMaxDeposits . into ( ) ) ;
@@ -172,18 +185,9 @@ impl Price {
172
185
let initial_percentage = diff_discount_precision_initial_as_price. div ( & discount_precision_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
173
186
let final_percentage = diff_discount_precision_final_as_price. div ( & discount_precision_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
174
187
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 ) ?;
187
191
188
192
// scale left and right to match expo; need to ensure no overflow so have a match for NoneEncountered error
189
193
if left. expo > right. expo {
@@ -209,7 +213,11 @@ impl Price {
209
213
}
210
214
}
211
215
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 ) ?;
213
221
214
222
// we want to scale the original confidence to the new expo
215
223
let conf_scaled = self . scale_confidence_to_exponent ( price_discounted. expo ) ;
@@ -1167,6 +1175,71 @@ mod test {
1167
1175
pc ( 90 * ( PD_SCALE as i64 ) , 2 * PD_SCALE , 0 )
1168
1176
) ;
1169
1177
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
+
1170
1243
// fails bc over max deposit limit
1171
1244
fails_exceeds_max_deposits (
1172
1245
pc ( 100 * ( PD_SCALE as i64 ) , 2 * PD_SCALE , 0 ) ,
0 commit comments