@@ -375,10 +375,10 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
375375 int32 expo1 ,
376376 int64 price2 ,
377377 int32 expo2 ,
378- int32 targetExpo ,
378+ uint8 targetDecimals ,
379379 int64 expectedPrice
380380 ) internal {
381- int64 price = PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetExpo );
381+ int64 price = PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetDecimals );
382382 assertEq (price, expectedPrice);
383383 }
384384
@@ -387,11 +387,11 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
387387 int32 expo1 ,
388388 int64 price2 ,
389389 int32 expo2 ,
390- int32 targetExpo ,
390+ uint8 targetDecimals ,
391391 bytes4 expectedError
392392 ) internal {
393393 vm.expectRevert (expectedError);
394- PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetExpo );
394+ PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetDecimals );
395395 }
396396
397397 function testConvertToUnit () public {
@@ -403,6 +403,18 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
403403 vm.expectRevert (PythErrors.InvalidInputExpo.selector );
404404 PythUtils.convertToUint (100 , - 256 , 18 );
405405
406+ // This test will fail as the 10 ** 237 is too large for a uint256
407+ vm.expectRevert (PythErrors.ExponentOverflow.selector );
408+ assertEq (PythUtils.convertToUint (100 , - 255 , 18 ), 0 );
409+
410+ // Combined Exponent can't be greater than 77
411+ vm.expectRevert (PythErrors.ExponentOverflow.selector );
412+ assertEq (PythUtils.convertToUint (100 , 60 , 18 ), 0 );
413+
414+ // Combined Exponent can't be less than -77
415+ vm.expectRevert (PythErrors.ExponentOverflow.selector );
416+ assertEq (PythUtils.convertToUint (100 , - 96 , 18 ), 0 );
417+
406418 // Negative Exponent Tests
407419 // Price with 18 decimals and exponent -5
408420 assertEq (
@@ -438,39 +450,76 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
438450
439451
440452 // Edge Cases
441- // This test will fail as the 10 ** 237 is too large for a uint256
442- // assertEq(PythUtils.convertToUint(100, -255, 18), 0);
443- // assertEq(PythUtils.convertToUint(100, 255, 18), 100_00_000_000_000_000_000_000_000);
453+ // 1. Test: price = 0, any expo/decimals returns 0
454+ assertEq (PythUtils.convertToUint (0 , - 77 , 0 ), 0 );
455+ assertEq (PythUtils.convertToUint (0 , 0 , 0 ), 0 );
456+ assertEq (PythUtils.convertToUint (0 , 77 , 0 ), 0 );
457+ assertEq (PythUtils.convertToUint (0 , - 77 , 77 ), 0 );
458+
459+ // 2. Test: smallest positive price, maximum downward exponent (should round to zero)
460+ assertEq (PythUtils.convertToUint (1 , - 77 , 0 ), 0 );
461+ assertEq (PythUtils.convertToUint (1 , - 77 , 77 ), 1 );
462+
463+ // 3. Test: combinedExpo == 0 (should be identical to price)
464+ assertEq (PythUtils.convertToUint (123456 , 0 , 0 ), 123456 );
465+ assertEq (PythUtils.convertToUint (123456 , - 5 , 5 ), 123456 ); // -5 + 5 == 0
466+
467+ // 4. Test: combinedExpo > 0 (should shift price up)
468+ assertEq (PythUtils.convertToUint (123456 , 5 , 0 ), 12345600000 );
469+ assertEq (PythUtils.convertToUint (123456 , 5 , 2 ), 1234560000000 );
470+
471+ // 5. Test: combinedExpo < 0 (should shift price down)
472+ assertEq (PythUtils.convertToUint (123456 , - 5 , 0 ), 1 );
473+ assertEq (PythUtils.convertToUint (123456 , - 5 , 2 ), 123 );
474+
475+ // 6. Test: division with truncation
476+ assertEq (PythUtils.convertToUint (999 , - 2 , 0 ), 9 ); // 999/100 = 9 (truncated)
477+ assertEq (PythUtils.convertToUint (199 , - 2 , 0 ), 1 ); // 199/100 = 1 (truncated)
478+ assertEq (PythUtils.convertToUint (99 , - 2 , 0 ), 0 ); // 99/100 = 0 (truncated)
479+
480+ // 7. Test: Big price and scaling, but outside of bounds
481+ vm.expectRevert (PythErrors.CombinedPriceOverflow.selector );
482+ assertEq (PythUtils.convertToUint (100_000_000 , 10 , 60 ),0 );
483+
484+ // 8. Test: Big price and scaling
485+ assertEq (PythUtils.convertToUint (100_000_000 , - 80 , 10 ),0 );
486+
487+ // 9. Test: Decimals just save from truncation
488+ assertEq (PythUtils.convertToUint (5 , - 1 , 1 ), 5 ); // 5/10*10 = 5
489+ assertEq (PythUtils.convertToUint (5 , - 1 , 2 ), 50 ); // 5/10*100 = 50
444490 }
445491
446- function testCombinePrices () public {
492+ function testDeriveCrossRate () public {
447493
448- // Basic Tests
449- assertCrossRateEquals ( 500 , - 8 , 500 , - 8 , - 5 , 100000 );
450- assertCrossRateEquals ( 10_000 , - 8 , 100 , - 2 , - 5 , 10 );
451- assertCrossRateEquals ( 10_000 , - 2 , 100 , - 8 , - 4 , 1_000_000_000_000 );
494+ // Test 1: Prices can't be negative
495+ assertCrossRateReverts ( - 100 , - 2 , 100 , - 2 , 5 , PythErrors.NegativeInputPrice. selector );
496+ assertCrossRateReverts ( 100 , - 2 , - 100 , - 2 , 5 , PythErrors.NegativeInputPrice. selector );
497+ assertCrossRateReverts ( - 100 , - 2 , - 100 , - 2 , 5 , PythErrors.NegativeInputPrice. selector );
452498
453- // Negative Price Tests
454- assertCrossRateReverts (- 100 , - 2 , 100 , - 2 , - 5 , PythErrors.NegativeInputPrice.selector );
455- assertCrossRateReverts (100 , - 2 , - 100 , - 2 , - 5 , PythErrors.NegativeInputPrice.selector );
456- assertCrossRateReverts (- 100 , - 2 , - 100 , - 2 , - 5 , PythErrors.NegativeInputPrice.selector );
457499
458- // Positive Exponent Tests
459- assertCrossRateReverts (100 , 2 , 100 , - 2 , - 5 , PythErrors.InvalidInputExpo.selector );
460- assertCrossRateReverts (100 , - 2 , 100 , 2 , - 5 , PythErrors.InvalidInputExpo.selector );
461- assertCrossRateReverts (100 , 2 , 100 , 2 , - 5 , PythErrors.InvalidInputExpo.selector );
500+ // Test 2: Exponent can't be positive
501+ assertCrossRateReverts (100 , 2 , 100 , - 2 , 5 , PythErrors.InvalidInputExpo.selector );
502+ assertCrossRateReverts (100 , - 2 , 100 , 2 , 5 , PythErrors.InvalidInputExpo.selector );
503+ assertCrossRateReverts (100 , 2 , 100 , 2 , 5 , PythErrors.InvalidInputExpo.selector );
504+
505+ // Test 3: Exponent can't be less than -255
506+ assertCrossRateReverts (100 , - 256 , 100 , - 2 , 5 , PythErrors.InvalidInputExpo.selector );
507+ assertCrossRateReverts (100 , - 2 , 100 , - 256 , 5 , PythErrors.InvalidInputExpo.selector );
462508
463- // Invalid Target Exponent Tests
464- assertCrossRateReverts (100 , - 2 , 100 , - 2 , 1 , PythErrors.InvalidTargetExpo.selector );
509+ // Test 4: Basic Tests
510+ assertCrossRateEquals (500 , - 8 , 500 , - 8 , 5 , 100000 );
511+ assertCrossRateEquals (10_000 , - 8 , 100 , - 2 , 5 , 10 );
512+ assertCrossRateEquals (10_000 , - 2 , 100 , - 8 , 5 , 1_000_000_000_000 );
465513
466- // Different Exponent Tests
467- assertCrossRateEquals (10_000 , - 2 , 100 , - 4 , - 4 , 100_000_000 );
468- assertCrossRateEquals (10_000 , - 2 , 10_000 , - 1 , - 2 , 10 );
514+ // Test 5: Different Exponent Tests
515+ assertCrossRateEquals (10_000 , - 2 , 100 , - 4 , 0 , 10_000 ); // 10_000 / 100 = 100 * 10(-2 - -4) = 10_000 with 0 decimals = 10_000
516+ assertCrossRateEquals (10_000 , - 2 , 100 , - 4 , 5 , 0 ); // 10_000 / 100 = 100 * 10(-2 - -4) = 10_000 with 5 decimals = 0
517+ assertCrossRateEquals (10_000 , - 2 , 10_000 , - 1 , 5 , 0 ); // It will truncate to 0
469518 assertCrossRateEquals (10_000 , - 10 , 10_000 , - 2 , 0 , 0 ); // It will truncate to 0
470519
471- // Exponent Edge Tests
472- assertCrossRateEquals (10_000 , 0 , 100 , 0 , 0 , 100 );
473- assertCrossRateEquals (10_000 , 0 , 100 , 0 , - 255 , 100 );
520+ // // Exponent Edge Tests
521+ // assertCrossRateEquals(10_000, 0, 100, 0, 0, 100);
522+ // assertCrossRateEquals(10_000, 0, 100, 0, -255, 100);
474523 // assertCrossRateEquals(10_000, 0, 100, -255, -255, 100, -255);
475524 // assertCrossRateEquals(10_000, -255, 100, 0, 0, 100, 0);
476525
0 commit comments