@@ -375,10 +375,10 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
375
375
int32 expo1 ,
376
376
int64 price2 ,
377
377
int32 expo2 ,
378
- int32 targetExpo ,
378
+ uint8 targetDecimals ,
379
379
int64 expectedPrice
380
380
) internal {
381
- int64 price = PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetExpo );
381
+ int64 price = PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetDecimals );
382
382
assertEq (price, expectedPrice);
383
383
}
384
384
@@ -387,11 +387,11 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
387
387
int32 expo1 ,
388
388
int64 price2 ,
389
389
int32 expo2 ,
390
- int32 targetExpo ,
390
+ uint8 targetDecimals ,
391
391
bytes4 expectedError
392
392
) internal {
393
393
vm.expectRevert (expectedError);
394
- PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetExpo );
394
+ PythUtils.deriveCrossRate (price1, expo1, price2, expo2, targetDecimals );
395
395
}
396
396
397
397
function testConvertToUnit () public {
@@ -403,6 +403,18 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
403
403
vm.expectRevert (PythErrors.InvalidInputExpo.selector );
404
404
PythUtils.convertToUint (100 , - 256 , 18 );
405
405
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
+
406
418
// Negative Exponent Tests
407
419
// Price with 18 decimals and exponent -5
408
420
assertEq (
@@ -438,39 +450,76 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
438
450
439
451
440
452
// 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
444
490
}
445
491
446
- function testCombinePrices () public {
492
+ function testDeriveCrossRate () public {
447
493
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 );
452
498
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 );
457
499
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 );
462
508
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 );
465
513
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
469
518
assertCrossRateEquals (10_000 , - 10 , 10_000 , - 2 , 0 , 0 ); // It will truncate to 0
470
519
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);
474
523
// assertCrossRateEquals(10_000, 0, 100, -255, -255, 100, -255);
475
524
// assertCrossRateEquals(10_000, -255, 100, 0, 0, 100, 0);
476
525
0 commit comments