@@ -371,21 +371,27 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
371371
372372contract PythUtilsTest is Test , WormholeTestUtils , PythTestUtils , IPythEvents {
373373 function assertCrossRateEquals (
374- int64 price1 ,
375- int32 expo1 ,
376- int64 price2 ,
374+ int64 price1 ,
375+ int32 expo1 ,
376+ int64 price2 ,
377377 int32 expo2 ,
378- int32 targetExpo ,
378+ int32 targetExpo ,
379379 uint256 expectedPrice
380380 ) internal {
381- uint256 price = PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetExpo);
381+ uint256 price = PythUtils.deriveCrossRate (
382+ price1,
383+ expo1,
384+ price2,
385+ expo2,
386+ targetExpo
387+ );
382388 assertEq (price, expectedPrice);
383389 }
384390
385391 function assertCrossRateReverts (
386- int64 price1 ,
387- int32 expo1 ,
388- int64 price2 ,
392+ int64 price1 ,
393+ int32 expo1 ,
394+ int64 price2 ,
389395 int32 expo2 ,
390396 int32 targetExpo ,
391397 bytes4 expectedError
@@ -395,7 +401,6 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
395401 }
396402
397403 function testConvertToUnit () public {
398-
399404 // Test 1: Price can't be negative
400405 vm.expectRevert (PythErrors.NegativeInputPrice.selector );
401406 PythUtils.convertToUint (- 100 , - 5 , 18 );
@@ -438,13 +443,15 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
438443
439444 // Test 8: Positive Exponent Tests
440445 // Price with 18 decimals and exponent 5
441- assertEq (PythUtils.convertToUint (100 , 5 , 18 ), 100_00_000_000_000_000_000_000_000 ); // 100 with 23 zeros
446+ assertEq (
447+ PythUtils.convertToUint (100 , 5 , 18 ),
448+ 100_00_000_000_000_000_000_000_000
449+ ); // 100 with 23 zeros
442450 // Test 9: Price with 9 decimals and exponent 2
443451 assertEq (PythUtils.convertToUint (100 , 2 , 9 ), 100_00_000_000_000 ); // 100 with 11 zeros
444452
445453 // Test 10: Price with 2 decimals and exponent 1
446- assertEq (PythUtils.convertToUint (100 , 1 , 2 ), 100_000 ); // 100 with 3 zeros
447-
454+ assertEq (PythUtils.convertToUint (100 , 1 , 2 ), 100_000 ); // 100 with 3 zeros
448455
449456 // Special Cases
450457 // Test 11: price = 0, any expo/decimals returns 0
@@ -464,7 +471,7 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
464471 // Test 14: deltaExponent > 0 (should shift price up)
465472 assertEq (PythUtils.convertToUint (123456 , 5 , 0 ), 12345600000 );
466473 assertEq (PythUtils.convertToUint (123456 , 5 , 2 ), 1234560000000 );
467-
474+
468475 // Test 15: deltaExponent < 0 (should shift price down)
469476 assertEq (PythUtils.convertToUint (123456 , - 5 , 0 ), 1 );
470477 assertEq (PythUtils.convertToUint (123456 , - 5 , 2 ), 123 );
@@ -476,22 +483,25 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
476483
477484 // Test 17: Big price and scaling, but outside of bounds
478485 vm.expectRevert (PythErrors.ExponentOverflow.selector );
479- assertEq (PythUtils.convertToUint (100_000_000 , 10 , 50 ),0 );
486+ assertEq (PythUtils.convertToUint (100_000_000 , 10 , 50 ), 0 );
480487
481488 // Test 18: Big price and scaling
482- assertEq (PythUtils.convertToUint (100_000_000 , - 50 , 10 ),0 ); // -50 + 10 = -40 > -58
483- vm.expectRevert (PythErrors.ExponentOverflow.selector );
489+ assertEq (PythUtils.convertToUint (100_000_000 , - 50 , 10 ), 0 ); // -50 + 10 = -40 > -58
490+ vm.expectRevert (PythErrors.ExponentOverflow.selector );
484491 assertEq (PythUtils.convertToUint (100_000_000 , 10 , 50 ), 0 ); // 10 + 50 = 60 > 58
485-
492+
486493 // Test 19: Decimals just save from truncation
487494 assertEq (PythUtils.convertToUint (5 , - 1 , 1 ), 5 ); // 5/10*10 = 5
488495 assertEq (PythUtils.convertToUint (5 , - 1 , 2 ), 50 ); // 5/10*100 = 50
489496
490497 // 10. Test: Big price and scaling, should be inside the bounds
491498 // We have to convert int64 -> int256 -> uint256 before multiplying by 10 ** 58
492- assertEq (PythUtils.convertToUint (type (int64 ).max, 50 , 8 ), uint256 (int256 (type (int64 ).max)) * 10 ** 58 ); // 50 + 8 = 58
499+ assertEq (
500+ PythUtils.convertToUint (type (int64 ).max, 50 , 8 ),
501+ uint256 (int256 (type (int64 ).max)) * 10 ** 58
502+ ); // 50 + 8 = 58
493503 vm.expectRevert (PythErrors.ExponentOverflow.selector );
494- assertEq (PythUtils.convertToUint (type (int64 ).max, 50 , 9 ), 0 );
504+ assertEq (PythUtils.convertToUint (type (int64 ).max, 50 , 9 ), 0 );
495505 assertEq (PythUtils.convertToUint (type (int64 ).max, - 64 , 8 ), 0 ); // -64 + 8 = -56 > -58
496506 assertEq (PythUtils.convertToUint (type (int64 ).max, - 50 , 1 ), 0 ); // -50 + 1 = -49 > -58
497507
@@ -500,77 +510,191 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
500510 assertEq (PythUtils.convertToUint (type (int64 ).max, 50 , 9 ), 0 ); // 50 + 9 = 59 > 58
501511 vm.expectRevert (PythErrors.ExponentOverflow.selector );
502512 assertEq (PythUtils.convertToUint (type (int64 ).max, - 60 , 1 ), 0 ); // -60 + 1 = -59 < -58
503-
504513 }
505514
506515 function testDeriveCrossRate () public {
507-
508516 // Test 1: Prices can't be negative
509- assertCrossRateReverts (- 100 , - 2 , 100 , - 2 , 5 , PythErrors.NegativeInputPrice.selector );
510- assertCrossRateReverts (100 , - 2 , - 100 , - 2 , 5 , PythErrors.NegativeInputPrice.selector );
511- assertCrossRateReverts (- 100 , - 2 , - 100 , - 2 , 5 , PythErrors.NegativeInputPrice.selector );
517+ assertCrossRateReverts (
518+ - 100 ,
519+ - 2 ,
520+ 100 ,
521+ - 2 ,
522+ 5 ,
523+ PythErrors.NegativeInputPrice.selector
524+ );
525+ assertCrossRateReverts (
526+ 100 ,
527+ - 2 ,
528+ - 100 ,
529+ - 2 ,
530+ 5 ,
531+ PythErrors.NegativeInputPrice.selector
532+ );
533+ assertCrossRateReverts (
534+ - 100 ,
535+ - 2 ,
536+ - 100 ,
537+ - 2 ,
538+ 5 ,
539+ PythErrors.NegativeInputPrice.selector
540+ );
512541
513542 // Test 2: Exponent can't be less than -255
514- assertCrossRateReverts (100 , - 256 , 100 , - 2 , 5 , PythErrors.InvalidInputExpo.selector );
515- assertCrossRateReverts (100 , - 2 , 100 , - 256 , 5 , PythErrors.InvalidInputExpo.selector );
516- assertCrossRateReverts (100 , - 256 , 100 , - 256 , 5 , PythErrors.InvalidInputExpo.selector );
543+ assertCrossRateReverts (
544+ 100 ,
545+ - 256 ,
546+ 100 ,
547+ - 2 ,
548+ 5 ,
549+ PythErrors.InvalidInputExpo.selector
550+ );
551+ assertCrossRateReverts (
552+ 100 ,
553+ - 2 ,
554+ 100 ,
555+ - 256 ,
556+ 5 ,
557+ PythErrors.InvalidInputExpo.selector
558+ );
559+ assertCrossRateReverts (
560+ 100 ,
561+ - 256 ,
562+ 100 ,
563+ - 256 ,
564+ 5 ,
565+ PythErrors.InvalidInputExpo.selector
566+ );
517567 // Target exponent can't be less than -255
518- assertCrossRateReverts (100 , - 2 , 100 , - 2 , - 256 , PythErrors.InvalidInputExpo.selector );
568+ assertCrossRateReverts (
569+ 100 ,
570+ - 2 ,
571+ 100 ,
572+ - 2 ,
573+ - 256 ,
574+ PythErrors.InvalidInputExpo.selector
575+ );
519576
520- // Test 3: Basic Tests with negative exponents
521- assertCrossRateEquals (500 , - 8 , 500 , - 8 , - 5 , 100000 );
577+ // Test 3: Basic Tests with negative exponents
578+ assertCrossRateEquals (500 , - 8 , 500 , - 8 , - 5 , 100000 );
522579 assertCrossRateEquals (10_000 , - 8 , 100 , - 2 , - 5 , 10 );
523580 assertCrossRateEquals (10_000 , - 2 , 100 , - 8 , - 5 , 100_00_000_000_000 );
524581
525582 // Test 4: Basic Tests with positive exponents
526583 assertCrossRateEquals (100 , 2 , 100 , 2 , - 5 , 100000 ); // 100 * 10^2 / 100 * 10^2 = 10000 / 10000 = 1 == 100000 * 10^-5
527584 // We will loose preistion as the the target exponent is 5 making the price 0.00001
528- assertCrossRateEquals (100 , 8 , 100 , 8 , 5 , 0 );
585+ assertCrossRateEquals (100 , 8 , 100 , 8 , 5 , 0 );
529586
530587 // Test 5: Different Exponent Tests
531588 assertCrossRateEquals (10_000 , - 2 , 100 , - 4 , 0 , 10_000 ); // 10_000 / 100 = 100 * 10(-2 - -4) = 10_000 with 0 decimals = 10_000
532589 assertCrossRateEquals (10_000 , - 2 , 100 , - 4 , 5 , 0 ); // 10_000 / 100 = 100 * 10(-2 - -4) = 10_000 with 5 decimals = 0
533590 assertCrossRateEquals (10_000 , - 2 , 10_000 , - 1 , 5 , 0 ); // It will truncate to 0
534591 assertCrossRateEquals (10_000 , - 10 , 10_000 , - 2 , 0 , 0 ); // It will truncate to 0
535- assertCrossRateEquals (100_000_000 , - 2 , 100 , - 8 , - 8 , 100_000_000_000_000_000_000 ); // 100_000_000 / 100 = 1_000_000 * 10(-2 - -8) = 1000000 * 10^6 = 1000000000000
592+ assertCrossRateEquals (
593+ 100_000_000 ,
594+ - 2 ,
595+ 100 ,
596+ - 8 ,
597+ - 8 ,
598+ 100_000_000_000_000_000_000
599+ ); // 100_000_000 / 100 = 1_000_000 * 10(-2 - -8) = 1000000 * 10^6 = 1000000000000
536600
537601 // Test 6: Exponent Edge Tests
538- assertCrossRateEquals (10_000 , 0 , 100 , 0 , 0 , 100 );
539- assertCrossRateReverts (10_000 , 0 , 100 , 0 , - 255 , PythErrors.ExponentOverflow.selector );
540- assertCrossRateReverts (10_000 , 0 , 100 , - 255 , - 255 , PythErrors.ExponentOverflow.selector );
541- assertCrossRateReverts (10_000 , - 255 , 100 , 0 , 0 , PythErrors.ExponentOverflow.selector );
542- assertCrossRateReverts (10_000 , - 255 , 100 , - 178 , - 5 , PythErrors.ExponentOverflow.selector );
543-
602+ assertCrossRateEquals (10_000 , 0 , 100 , 0 , 0 , 100 );
603+ assertCrossRateReverts (
604+ 10_000 ,
605+ 0 ,
606+ 100 ,
607+ 0 ,
608+ - 255 ,
609+ PythErrors.ExponentOverflow.selector
610+ );
611+ assertCrossRateReverts (
612+ 10_000 ,
613+ 0 ,
614+ 100 ,
615+ - 255 ,
616+ - 255 ,
617+ PythErrors.ExponentOverflow.selector
618+ );
619+ assertCrossRateReverts (
620+ 10_000 ,
621+ - 255 ,
622+ 100 ,
623+ 0 ,
624+ 0 ,
625+ PythErrors.ExponentOverflow.selector
626+ );
627+ assertCrossRateReverts (
628+ 10_000 ,
629+ - 255 ,
630+ 100 ,
631+ - 178 ,
632+ - 5 ,
633+ PythErrors.ExponentOverflow.selector
634+ );
544635
545636 // Test 7: Max int64 price and scaling
546- assertCrossRateEquals (type (int64 ).max, 0 , 1 , 0 , 0 , uint256 (int256 (type (int64 ).max)));
637+ assertCrossRateEquals (
638+ type (int64 ).max,
639+ 0 ,
640+ 1 ,
641+ 0 ,
642+ 0 ,
643+ uint256 (int256 (type (int64 ).max))
644+ );
547645 assertCrossRateEquals (1 , 0 , type (int64 ).max, 0 , 0 , 0 );
548646 assertCrossRateEquals (type (int64 ).max, 0 , type (int64 ).max, 0 , 0 , 1 );
549647 // type(int64).max is approx 9.223e18
550648 assertCrossRateEquals (type (int64 ).max, 0 , 1 , 0 , 18 , 9 );
551649 // 1 / type(int64).max is approx 1.085e-19
552650 assertCrossRateEquals (1 , 0 , type (int64 ).max, 0 , - 19 , 1 );
553651 // type(int64).max * 10 ** 58 / 1
554- assertCrossRateEquals (type (int64 ).max, 50 , 1 , - 8 , 0 , uint256 (int256 (type (int64 ).max)) * 10 ** 58 );
652+ assertCrossRateEquals (
653+ type (int64 ).max,
654+ 50 ,
655+ 1 ,
656+ - 8 ,
657+ 0 ,
658+ uint256 (int256 (type (int64 ).max)) * 10 ** 58
659+ );
555660 // 1 / (type(int64).max * 10 ** 58)
556661 assertCrossRateEquals (1 , 0 , type (int64 ).max, 50 , 8 , 0 );
557662
558663 // type(int64).max * 10 ** 59 / 1
559- assertCrossRateReverts (type (int64 ).max, 50 , 1 , - 9 , 0 , PythErrors.ExponentOverflow.selector );
664+ assertCrossRateReverts (
665+ type (int64 ).max,
666+ 50 ,
667+ 1 ,
668+ - 9 ,
669+ 0 ,
670+ PythErrors.ExponentOverflow.selector
671+ );
560672 // 1 / (type(int64).max * 10 ** 59)
561- assertCrossRateReverts (1 , 0 , type (int64 ).max, 50 , 9 , PythErrors.ExponentOverflow.selector );
562-
673+ assertCrossRateReverts (
674+ 1 ,
675+ 0 ,
676+ type (int64 ).max,
677+ 50 ,
678+ 9 ,
679+ PythErrors.ExponentOverflow.selector
680+ );
563681
564682 // Realistic Tests
565683 // Test case 1: (StEth/Eth / Eth/USD = ETH/BTC)
566- uint256 price = PythUtils.deriveCrossRate (206487956502 , - 8 , 206741615681 , - 8 , - 8 );
684+ uint256 price = PythUtils.deriveCrossRate (
685+ 206487956502 ,
686+ - 8 ,
687+ 206741615681 ,
688+ - 8 ,
689+ - 8
690+ );
567691 assertApproxEqRel (price, 100000000 , 9e17 ); // $1
568692
569- // Test case 2:
693+ // Test case 2:
570694 price = PythUtils.deriveCrossRate (520010 , - 8 , 38591 , - 8 , - 8 );
571695 assertApproxEqRel (price, 1347490347 , 9e17 ); // $13.47
572696
573- // Test case 3:
697+ // Test case 3:
574698 price = PythUtils.deriveCrossRate (520010 , - 8 , 38591 , - 8 , - 12 );
575699 assertApproxEqRel (price, 13474903475432 , 9e17 ); // $13.47
576700 }
0 commit comments