@@ -108,7 +108,7 @@ impl Price {
108
108
/// discount_precision: u64, the precision used for discounts
109
109
///
110
110
/// Logic
111
- /// collateral_valuation_price = (deposits * price * (discount_precision-discount_final) + (max_deposits - deposits) * price * (discount_precision - discount_initial)) / (max_deposits * discount_precision)
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
112
112
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
113
if max_deposits < deposits {
114
114
return Err ( LiquidityOracleError :: ExceedsMaxDeposits . into ( ) ) ;
@@ -126,44 +126,90 @@ impl Price {
126
126
let diff_discount_precision_initial = ( discount_precision-discount_initial) as i64 ;
127
127
let diff_discount_precision_final = ( discount_precision-discount_final) as i64 ;
128
128
129
- let mut left = self . cmul ( deposits as i64 , 0 ) .
129
+ // get fractions for deposits
130
+ let deposits_as_price = Price {
131
+ price : deposits as i64 ,
132
+ conf : 0 ,
133
+ expo : 0 ,
134
+ publish_time : 0 ,
135
+ } ;
136
+ let remaining_depositable_as_price = Price {
137
+ price : remaining_depositable,
138
+ conf : 0 ,
139
+ expo : 0 ,
140
+ publish_time : 0 ,
141
+ } ;
142
+ let max_deposits_as_price = Price {
143
+ price : max_deposits as i64 ,
144
+ conf : 1 ,
145
+ expo : 0 ,
146
+ publish_time : 0 ,
147
+ } ;
148
+
149
+ let deposits_percentage = deposits_as_price. div ( & max_deposits_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
150
+ let remaining_depositable_percentage = remaining_depositable_as_price. div ( & max_deposits_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
151
+
152
+ // get fractions for discount
153
+ let diff_discount_precision_initial_as_price = Price {
154
+ price : diff_discount_precision_initial,
155
+ conf : 0 ,
156
+ expo : 0 ,
157
+ publish_time : 0 ,
158
+ } ;
159
+ let diff_discount_precision_final_as_price = Price {
160
+ price : diff_discount_precision_final,
161
+ conf : 0 ,
162
+ expo : 0 ,
163
+ publish_time : 0 ,
164
+ } ;
165
+ let discount_precision_as_price = Price {
166
+ price : discount_precision as i64 ,
167
+ conf : 1 ,
168
+ expo : 0 ,
169
+ publish_time : 0 ,
170
+ } ;
171
+
172
+ let initial_percentage = diff_discount_precision_initial_as_price. div ( & discount_precision_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
173
+ let final_percentage = diff_discount_precision_final_as_price. div ( & discount_precision_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
174
+
175
+
176
+ // compute left and right terms of the sum
177
+ let mut left = self . mul ( & deposits_percentage) .
130
178
ok_or ( LiquidityOracleError :: NoneEncountered ) ?.
131
- cmul ( diff_discount_precision_final , 0 ) .
179
+ mul ( & final_percentage ) .
132
180
ok_or ( LiquidityOracleError :: NoneEncountered ) ?
133
181
;
134
- let mut right = self . cmul ( remaining_depositable , 0 ) .
182
+ let mut right = self . mul ( & remaining_depositable_percentage ) .
135
183
ok_or ( LiquidityOracleError :: NoneEncountered ) ?.
136
- cmul ( diff_discount_precision_initial , 0 ) .
184
+ mul ( & initial_percentage ) .
137
185
ok_or ( LiquidityOracleError :: NoneEncountered ) ?
138
186
;
139
- // scale left and right to match expo
187
+
188
+ // scale left and right to match expo; need to ensure no overflow so have a match for NoneEncountered error
140
189
if left. expo > right. expo {
141
- left = left.
142
- scale_to_exponent ( right. expo ) .
143
- ok_or ( LiquidityOracleError :: NoneEncountered ) ?
144
- ;
190
+ // prefer right scaled up to left if no overflow, to prevent any precision loss
191
+ let right_scaled = right. scale_to_exponent ( left. expo ) ;
192
+ let left_scaled = left. scale_to_exponent ( right. expo ) ;
193
+
194
+ match right_scaled {
195
+ Some ( _x) => right = right_scaled. ok_or ( LiquidityOracleError :: NoneEncountered ) ?,
196
+
197
+ None => left = left_scaled. ok_or ( LiquidityOracleError :: NoneEncountered ) ?,
198
+ }
145
199
}
146
200
else if left. expo < right. expo {
147
- right = right.
148
- scale_to_exponent ( left. expo ) .
149
- ok_or ( LiquidityOracleError :: NoneEncountered ) ?
150
- ;
151
- }
201
+ // prefer left scaled up to right if no overflow, to prevent any precision loss
202
+ let left_scaled = left. scale_to_exponent ( right. expo ) ;
203
+ let right_scaled = right. scale_to_exponent ( left. expo ) ;
152
204
153
- let numer = left . add ( & right ) . ok_or ( LiquidityOracleError :: NoneEncountered ) ? ;
154
- let denom = max_deposits * discount_precision ;
205
+ match left_scaled {
206
+ Some ( _x ) => left = left_scaled . ok_or ( LiquidityOracleError :: NoneEncountered ) ? ,
155
207
156
- // TODO: divide price by a constant (denom)
157
- // can denote denom as a Price with tiny confidence,
158
- // perform the div, and throw away the resulting confidence,
159
- // since all we care about from the div is price and expo
160
- let denom_as_price = Price {
161
- price : denom as i64 ,
162
- conf : 1 ,
163
- expo : 0 ,
164
- publish_time : self . publish_time ,
165
- } ;
166
- let price_discounted = numer. div ( & denom_as_price) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
208
+ None => right = right_scaled. ok_or ( LiquidityOracleError :: NoneEncountered ) ?,
209
+ }
210
+ }
211
+
212
+ let price_discounted = left. add ( & right) . ok_or ( LiquidityOracleError :: NoneEncountered ) ?;
167
213
168
214
// we want to scale the original confidence to the new expo
169
215
let conf_scaled = self . scale_confidence_to_exponent ( price_discounted. expo ) ;
@@ -1023,8 +1069,6 @@ mod test {
1023
1069
fn test_get_collateral_valuation_price ( ) {
1024
1070
fn succeeds ( price : Price , deposits : u64 , max_deposits : u64 , discount_initial : u64 , discount_final : u64 , discount_precision : u64 , mut expected : Price ) {
1025
1071
let mut price_collat = price. get_collateral_valuation_price ( deposits, max_deposits, discount_initial, discount_final, discount_precision) . unwrap ( ) ;
1026
- print ! ( "Output: price is {}, conf is {}, expo is {}, ts is {}" , price_collat. price, price_collat. conf, price_collat. expo, price_collat. publish_time) ;
1027
- print ! ( "Exepcted: price is {}, conf is {}, expo is {}, ts is {}" , expected. price, expected. conf, expected. expo, expected. publish_time) ;
1028
1072
1029
1073
// scale price_collat and expected to match in expo
1030
1074
if price_collat. expo > expected. expo {
0 commit comments