Skip to content

Commit 017cad1

Browse files
committed
made changes to accomodate precision issues, handled overflow bugs
1 parent e7ab5d8 commit 017cad1

File tree

1 file changed

+74
-30
lines changed

1 file changed

+74
-30
lines changed

pyth-sdk/src/price.rs

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl Price {
108108
/// discount_precision: u64, the precision used for discounts
109109
///
110110
/// 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
112112
pub fn get_collateral_valuation_price(&self, deposits: u64, max_deposits: u64, discount_initial: u64, discount_final: u64, discount_precision: u64) -> Result<Price, LiquidityOracleError> {
113113
if max_deposits < deposits {
114114
return Err(LiquidityOracleError::ExceedsMaxDeposits.into());
@@ -126,44 +126,90 @@ impl Price {
126126
let diff_discount_precision_initial = (discount_precision-discount_initial) as i64;
127127
let diff_discount_precision_final = (discount_precision-discount_final) as i64;
128128

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).
130178
ok_or(LiquidityOracleError::NoneEncountered)?.
131-
cmul(diff_discount_precision_final, 0).
179+
mul(&final_percentage).
132180
ok_or(LiquidityOracleError::NoneEncountered)?
133181
;
134-
let mut right = self.cmul(remaining_depositable, 0).
182+
let mut right = self.mul(&remaining_depositable_percentage).
135183
ok_or(LiquidityOracleError::NoneEncountered)?.
136-
cmul(diff_discount_precision_initial, 0).
184+
mul(&initial_percentage).
137185
ok_or(LiquidityOracleError::NoneEncountered)?
138186
;
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
140189
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+
}
145199
}
146200
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);
152204

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)?,
155207

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)?;
167213

168214
// we want to scale the original confidence to the new expo
169215
let conf_scaled = self.scale_confidence_to_exponent(price_discounted.expo);
@@ -1023,8 +1069,6 @@ mod test {
10231069
fn test_get_collateral_valuation_price() {
10241070
fn succeeds(price: Price, deposits: u64, max_deposits: u64, discount_initial: u64, discount_final: u64, discount_precision: u64, mut expected: Price) {
10251071
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);
10281072

10291073
// scale price_collat and expected to match in expo
10301074
if price_collat.expo > expected.expo {

0 commit comments

Comments
 (0)