@@ -2,6 +2,7 @@ use std::cmp::Ordering;
22
33use ethers:: types:: I256 ;
44use fixed_point:: FixedPoint ;
5+ use eyre:: { eyre, Result } ;
56use fixed_point_macros:: fixed;
67
78use crate :: { calculate_effective_share_reserves, State , YieldSpace } ;
@@ -294,6 +295,7 @@ impl State {
294295 } ;
295296 if self
296297 . solvency_after_short ( absolute_max_bond_amount, spot_price, checkpoint_exposure)
298+ . unwrap ( )
297299 . is_some ( )
298300 {
299301 return absolute_max_bond_amount;
@@ -316,9 +318,9 @@ impl State {
316318 //
317319 // The guess that we make is very important in determining how quickly
318320 // we converge to the solution.
319- let mut max_bond_amount = self . absolute_max_short_guess ( spot_price, checkpoint_exposure) ;
321+ let mut max_bond_amount = self . absolute_max_short_guess ( spot_price, checkpoint_exposure) . unwrap ( ) ;
320322 let mut maybe_solvency =
321- self . solvency_after_short ( max_bond_amount, spot_price, checkpoint_exposure) ;
323+ self . solvency_after_short ( max_bond_amount, spot_price, checkpoint_exposure) . unwrap ( ) ;
322324 if maybe_solvency. is_none ( ) {
323325 panic ! ( "Initial guess in `absolute_max_short` is insolvent." ) ;
324326 }
@@ -348,7 +350,7 @@ impl State {
348350 possible_max_bond_amount,
349351 spot_price,
350352 checkpoint_exposure,
351- ) ;
353+ ) . unwrap ( ) ;
352354 if let Some ( s) = maybe_solvency {
353355 solvency = s;
354356 max_bond_amount = possible_max_bond_amount;
@@ -387,13 +389,13 @@ impl State {
387389 & self ,
388390 spot_price : FixedPoint ,
389391 checkpoint_exposure : I256 ,
390- ) -> FixedPoint {
392+ ) -> Result < FixedPoint > {
391393 let estimate_price = spot_price;
392394 let checkpoint_exposure =
393395 FixedPoint :: from ( checkpoint_exposure. max ( I256 :: zero ( ) ) ) / self . vault_share_price ( ) ;
394- ( self . vault_share_price ( ) * ( self . calculate_solvency ( ) + checkpoint_exposure) )
396+ Ok ( ( self . vault_share_price ( ) * ( self . calculate_solvency ( ) + checkpoint_exposure) )
395397 / ( estimate_price - self . curve_fee ( ) * ( fixed ! ( 1e18 ) - spot_price)
396- + self . governance_lp_fee ( ) * self . curve_fee ( ) * ( fixed ! ( 1e18 ) - spot_price) )
398+ + self . governance_lp_fee ( ) * self . curve_fee ( ) * ( fixed ! ( 1e18 ) - spot_price) ) )
397399 }
398400
399401 /// Calculates the pool's solvency after opening a short.
@@ -432,11 +434,11 @@ impl State {
432434 bond_amount : FixedPoint ,
433435 spot_price : FixedPoint ,
434436 checkpoint_exposure : I256 ,
435- ) -> Option < FixedPoint > {
437+ ) -> Result < Option < FixedPoint > > {
436438 let principal = if let Ok ( p) = self . calculate_short_principal ( bond_amount) {
437439 p
438440 } else {
439- return None ;
441+ return Ok ( None ) ;
440442 } ;
441443 let share_reserves = self . share_reserves ( )
442444 - ( principal
@@ -449,12 +451,15 @@ impl State {
449451 // / self.vault_share_price();
450452 let exposure = {
451453 let checkpoint_exposure: FixedPoint = checkpoint_exposure. max ( I256 :: zero ( ) ) . into ( ) ;
454+ if self . long_exposure ( ) <= checkpoint_exposure {
455+ return Err ( eyre ! ( "InsufficientLongExposure" , ) ) ;
456+ }
452457 ( self . long_exposure ( ) - checkpoint_exposure) / self . vault_share_price ( )
453458 } ;
454459 if share_reserves >= exposure + self . minimum_share_reserves ( ) {
455- Some ( share_reserves - exposure - self . minimum_share_reserves ( ) )
460+ Ok ( Some ( share_reserves - exposure - self . minimum_share_reserves ( ) ) )
456461 } else {
457- None
462+ Ok ( None )
458463 }
459464 }
460465
@@ -519,11 +524,24 @@ mod tests {
519524 #[ tokio:: test]
520525 async fn fuzz_calculate_max_short_no_budget ( ) -> Result < ( ) > {
521526 let chain = TestChain :: new ( ) . await ?;
527+ let mut lowest_rate: Option < FixedPoint > = None ;
528+ let mut highest_rate: Option < FixedPoint > = None ;
529+
530+ let mut passed_tests = 0 ;
531+ let mut failed_tests = 0 ;
522532
523533 // Fuzz the rust and solidity implementations against each other.
524534 let mut rng = thread_rng ( ) ;
525535 for _ in 0 ..* FAST_FUZZ_RUNS {
526536 let state = rng. gen :: < State > ( ) ;
537+ let fixed_rate = state. calculate_spot_rate ( ) ;
538+ if lowest_rate. is_none ( ) || fixed_rate < lowest_rate. unwrap ( ) {
539+ lowest_rate = Some ( fixed_rate) ;
540+ }
541+ if highest_rate. is_none ( ) || fixed_rate > highest_rate. unwrap ( ) {
542+ highest_rate = Some ( fixed_rate) ;
543+ }
544+ println ! ( "Fixed rate: {}" , fixed_rate) ;
527545 let checkpoint_exposure = {
528546 let value = rng. gen_range ( fixed ! ( 0 ) ..=fixed ! ( 10_000_000e18 ) ) ;
529547 if rng. gen ( ) {
@@ -567,10 +585,18 @@ mod tests {
567585 . await
568586 {
569587 Ok ( expected) => {
588+ passed_tests += 1 ;
570589 assert_eq ! ( actual. unwrap( ) , FixedPoint :: from( expected) ) ;
571590 }
572- Err ( _) => assert ! ( actual. is_err( ) ) ,
573- }
591+ Err ( expected) => {
592+ failed_tests += 1 ;
593+ println ! ( "Test failed: actual: {:?} expected: {:?}" , actual, expected) ;
594+ assert ! ( actual. is_err( ) ) ;
595+ }
596+ } // end for loop
597+ let total_tests = failed_tests + passed_tests;
598+ let failure_rate = failed_tests as f64 / total_tests as f64 ;
599+ println ! ( "Total tests: {} Passed: {} Failed: {}, Failure rate: {}" , total_tests, passed_tests, failed_tests, failure_rate) ;
574600 }
575601
576602 Ok ( ( ) )
@@ -596,7 +622,6 @@ mod tests {
596622
597623 // Fund Alice and Bob.
598624 let fixed_rate = rng. gen_range ( fixed ! ( 0.01e18 ) ..=fixed ! ( 1e18 ) ) ;
599- println ! ( "Fixed rate: {}" , fixed_rate) ;
600625 let contribution = rng. gen_range ( fixed ! ( 100_000e18 ) ..=fixed ! ( 100_000_000e18 ) ) ;
601626 alice. fund ( contribution) . await ?;
602627
0 commit comments