@@ -557,13 +557,13 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
557557 mockUSDFC,
558558 address (pdpServiceWithPayments),
559559 true , // approved
560- 1000e6 , // rate allowance (1000 USDFC)
561- 1000e6 , // lockup allowance (1000 USDFC)
560+ 1000e18 , // rate allowance (1000 USDFC)
561+ 1000e18 , // lockup allowance (1000 USDFC)
562562 365 days // max lockup period
563563 );
564564
565565 // Client deposits funds to the FilecoinPayV1 contract for future payments
566- uint256 depositAmount = 10e6 ; // Sufficient funds for initial lockup and future operations
566+ uint256 depositAmount = 10e18 ; // Sufficient funds for initial lockup and future operations
567567 mockUSDFC.approve (address (payments), depositAmount);
568568 payments.deposit (mockUSDFC, client, depositAmount);
569569 vm.stopPrank ();
@@ -682,13 +682,13 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
682682 mockUSDFC,
683683 address (pdpServiceWithPayments),
684684 true , // approved
685- 1000e6 , // rate allowance (1000 USDFC)
686- 1000e6 , // lockup allowance (1000 USDFC)
685+ 1000e18 , // rate allowance (1000 USDFC)
686+ 1000e18 , // lockup allowance (1000 USDFC)
687687 365 days // max lockup period
688688 );
689689
690690 // Client deposits funds to the FilecoinPayV1 contract for future payments
691- uint256 depositAmount = 10e6 ; // Sufficient funds for initial lockup and future operations
691+ uint256 depositAmount = 10e18 ; // Sufficient funds for initial lockup and future operations
692692 mockUSDFC.approve (address (payments), depositAmount);
693693 payments.deposit (mockUSDFC, client, depositAmount);
694694 vm.stopPrank ();
@@ -795,11 +795,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
795795 mockUSDFC,
796796 address (pdpServiceWithPayments),
797797 true , // approved
798- 1000e6 , // rate allowance (1000 USDFC)
799- 1000e6 , // lockup allowance (1000 USDFC)
798+ 1000e18 , // rate allowance (1000 USDFC)
799+ 1000e18 , // lockup allowance (1000 USDFC)
800800 365 days // max lockup period
801801 );
802- uint256 depositAmount = 10e6 ; // Sufficient funds for initial lockup and future operations
802+ uint256 depositAmount = 10e18 ; // Sufficient funds for initial lockup and future operations
803803 mockUSDFC.approve (address (payments), depositAmount);
804804 payments.deposit (mockUSDFC, client, depositAmount);
805805 vm.stopPrank ();
@@ -909,38 +909,149 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
909909 // Test the values returned by getServicePrice
910910 FilecoinWarmStorageService.ServicePricing memory pricing = pdpServiceWithPayments.getServicePrice ();
911911
912- uint256 decimals = 6 ; // MockUSDFC uses 6 decimals in tests
913- uint256 expectedNoCDN = 25 * 10 ** (decimals - 1 ); // 2.5 USDFC with 6 decimals
914- uint256 expectedWithCDN = 3 * 10 ** decimals; // 3 USDFC with 6 decimals (2.5 + 0.5 CDN)
912+ uint256 decimals = 18 ; // MockUSDFC uses 18 decimals in tests
913+ uint256 expectedNoCDN = 25 * 10 ** (decimals - 1 ); // 2.5 USDFC with 18 decimals
914+ uint256 expectedWithCDN = 3 * 10 ** decimals; // 3 USDFC with 18 decimals (2.5 + 0.5 CDN)
915915
916916 assertEq (pricing.pricePerTiBPerMonthNoCDN, expectedNoCDN, "No CDN price should be 2.5 * 10^decimals " );
917917 assertEq (pricing.pricePerTiBPerMonthWithCDN, expectedWithCDN, "With CDN price should be 3 * 10^decimals " );
918918 assertEq (address (pricing.tokenAddress), address (mockUSDFC), "Token address should match USDFC " );
919919 assertEq (pricing.epochsPerMonth, 86400 , "Epochs per month should be 86400 " );
920920
921921 // Verify the values are in expected range
922- assert (pricing.pricePerTiBPerMonthNoCDN < 10 ** 8 ); // Less than 10^8
923- assert (pricing.pricePerTiBPerMonthWithCDN < 10 ** 8 ); // Less than 10^8
922+ assert (pricing.pricePerTiBPerMonthNoCDN < 10 ** 20 ); // Less than 10^20
923+ assert (pricing.pricePerTiBPerMonthWithCDN < 10 ** 20 ); // Less than 10^20
924924 }
925925
926926 function testGetEffectiveRatesValues () public view {
927927 // Test the values returned by getEffectiveRates
928928 (uint256 serviceFee , uint256 spPayment ) = pdpServiceWithPayments.getEffectiveRates ();
929929
930- uint256 decimals = 6 ; // MockUSDFC uses 6 decimals in tests
931- // Total is 2.5 USDFC with 6 decimals
930+ uint256 decimals = 18 ; // MockUSDFC uses 18 decimals in tests
931+ // Total is 2.5 USDFC with 18 decimals
932932 uint256 expectedTotal = 25 * 10 ** (decimals - 1 );
933933
934934 // Test setup uses 0% commission
935935 uint256 expectedServiceFee = 0 ; // 0% commission
936936 uint256 expectedSpPayment = expectedTotal; // 100% goes to SP
937937
938938 assertEq (serviceFee, expectedServiceFee, "Service fee should be 0 with 0% commission " );
939- assertEq (spPayment, expectedSpPayment, "SP payment should be 2.5 * 10^6 " );
940- assertEq (serviceFee + spPayment, expectedTotal, "Total should equal 2.5 * 10^6 " );
939+ assertEq (spPayment, expectedSpPayment, "SP payment should be 2.5 * 10^18 " );
940+ assertEq (serviceFee + spPayment, expectedTotal, "Total should equal 2.5 * 10^18 " );
941941
942942 // Verify the values are in expected range
943- assert (serviceFee + spPayment < 10 ** 8 ); // Less than 10^8
943+ assert (serviceFee + spPayment < 10 ** 20 ); // Less than 10^20
944+ }
945+
946+ // Minimum Pricing Tests
947+ function testMinimumPricing_SmallDataSetsPayFloorRate () public view {
948+ // Small datasets should all pay the minimum floor rate of 0.06 USDFC/month
949+ uint256 decimals = 18 ;
950+ uint256 oneGiB = 1024 * 1024 * 1024 ;
951+
952+ // Expected minimum: 0.06 USDFC/month = 6/100 with 18 decimals
953+ uint256 expectedMinPerMonth = (6 * 10 ** decimals) / 100 ;
954+ uint256 expectedMinPerEpoch = expectedMinPerMonth / 86400 ; // Convert to per-epoch
955+
956+ // Test 0 bytes
957+ (uint256 rateZero ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (0 );
958+ assertEq (rateZero, expectedMinPerEpoch, "0 bytes should return 0.06 USDFC/month minimum " );
959+
960+ // Test 1 GiB
961+ (uint256 rateOneGiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (oneGiB);
962+ assertEq (rateOneGiB, expectedMinPerEpoch, "1 GiB should return minimum rate " );
963+
964+ // Test 10 GiB
965+ (uint256 rateTenGiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (10 * oneGiB);
966+ assertEq (rateTenGiB, expectedMinPerEpoch, "10 GiB should return minimum rate " );
967+
968+ // Test 24 GiB (below crossover)
969+ (uint256 rateTwentyFourGiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (24 * oneGiB);
970+ assertEq (rateTwentyFourGiB, expectedMinPerEpoch, "24 GiB should return minimum rate " );
971+ }
972+
973+ function testMinimumPricing_CrossoverPoint () public view {
974+ // Test the crossover where natural pricing exceeds minimum
975+ // At 2.5 USDFC/TiB: 0.06/2.5*1024 = 24.576 GiB is the crossover
976+ uint256 oneGiB = 1024 * 1024 * 1024 ;
977+ uint256 decimals = 18 ;
978+ uint256 expectedMinPerMonth = (6 * 10 ** decimals) / 100 ;
979+ uint256 expectedMinPerEpoch = expectedMinPerMonth / 86400 ;
980+
981+ // 24 GiB: natural rate (0.0586) < minimum (0.06), so returns minimum
982+ (uint256 rate24GiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (24 * oneGiB);
983+ assertEq (rate24GiB, expectedMinPerEpoch, "24 GiB should use minimum floor " );
984+
985+ // 25 GiB: natural rate (0.0610) > minimum (0.06), so returns natural rate
986+ (uint256 rate25GiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (25 * oneGiB);
987+ assert (rate25GiB > expectedMinPerEpoch);
988+
989+ // Verify it's actually proportional (not minimum)
990+ uint256 expectedNatural25 = rate25GiB * 86400 ; // Convert to monthly
991+ uint256 expected25Monthly = (25 * 10 ** decimals * 25 ) / (1024 * 10 ); // 25 GiB at 2.5 USDFC/TiB
992+ // Tolerance: actual loss is ~16,000 from integer division, allow 100,000 for safety
993+ assertApproxEqAbs (expectedNatural25, expected25Monthly, 100000 , "25 GiB should use natural rate " );
994+ }
995+
996+ function testMinimumPricing_LargeDataSetsUseProportionalPricing () public view {
997+ // Large datasets should use proportional pricing (natural rate > minimum)
998+ uint256 oneGiB = 1024 * 1024 * 1024 ;
999+ uint256 decimals = 18 ;
1000+ uint256 expectedMinPerMonth = (6 * 10 ** decimals) / 100 ;
1001+ uint256 expectedMinPerEpoch = expectedMinPerMonth / 86400 ;
1002+
1003+ // Test 48 GiB
1004+ (uint256 rate48GiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (48 * oneGiB);
1005+ assert (rate48GiB > expectedMinPerEpoch);
1006+
1007+ // Test 100 GiB
1008+ (uint256 rate100GiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (100 * oneGiB);
1009+ assert (rate100GiB > rate48GiB);
1010+
1011+ // Test 1 TiB
1012+ uint256 oneTiB = oneGiB * 1024 ;
1013+ (uint256 rateOneTiB ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (oneTiB);
1014+ assert (rateOneTiB > rate100GiB);
1015+
1016+ // Verify proportional scaling
1017+ assertApproxEqRel (rate100GiB, rate48GiB * 100 / 48 , 0.01e18 , "Rates should scale proportionally " );
1018+ }
1019+
1020+ function testMinimumPricing_ExactlyPoint06USDFC () public view {
1021+ // Verify that minimum pricing is exactly 0.06 USDFC/month for small datasets
1022+ uint256 decimals = 18 ; // MockUSDFC uses 18 decimals in tests
1023+ uint256 oneGiB = 1024 * 1024 * 1024 ;
1024+
1025+ // Get rate per epoch for dataset below crossover point
1026+ (uint256 ratePerEpoch ,,) = pdpServiceWithPayments.calculateRatesPerEpoch (oneGiB);
1027+
1028+ // Convert to rate per month (86400 epochs per month)
1029+ uint256 ratePerMonth = ratePerEpoch * 86400 ;
1030+
1031+ // Expected: exactly 0.06 USDFC with 18 decimals = 60000000000000000
1032+ // Allow tiny tolerance for integer division rounding (0.06 / 86400 rounds down)
1033+ uint256 expected = (6 * 10 ** decimals) / 100 ;
1034+ uint256 tolerance = 1 ; // Allow 1 per epoch difference = 86400 total
1035+
1036+ assertApproxEqAbs (ratePerMonth, expected, tolerance * 86400 , "Minimum rate should be 0.06 USDFC/month " );
1037+ }
1038+
1039+ function testMinimumPricing_CDNRatesUnaffected () public view {
1040+ // Verify that CDN rates remain proportional (not affected by minimum pricing)
1041+ uint256 oneGiB = 1024 * 1024 * 1024 ;
1042+
1043+ // Get rates for small dataset
1044+ (, uint256 cacheMissRateSmall , uint256 cdnRateSmall ) = pdpServiceWithPayments.calculateRatesPerEpoch (oneGiB);
1045+
1046+ // Get rates for larger dataset (12 GiB)
1047+ (, uint256 cacheMissRateLarge , uint256 cdnRateLarge ) =
1048+ pdpServiceWithPayments.calculateRatesPerEpoch (12 * oneGiB);
1049+
1050+ // CDN rates should scale proportionally with size (allow small rounding tolerance)
1051+ assertApproxEqAbs (
1052+ cacheMissRateLarge, cacheMissRateSmall * 12 , 100 , "Cache miss rate should scale proportionally "
1053+ );
1054+ assertApproxEqAbs (cdnRateLarge, cdnRateSmall * 12 , 100 , "CDN rate should scale proportionally " );
9441055 }
9451056
9461057 uint256 nextClientDataSetId = 0 ;
@@ -971,9 +1082,9 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
9711082
9721083 // Setup client payment approval if not already done
9731084 vm.startPrank (clientAddress);
974- payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e6 , 1000e6 , 365 days);
975- mockUSDFC.approve (address (payments), 100e6 );
976- payments.deposit (mockUSDFC, clientAddress, 100e6 );
1085+ payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e18 , 1000e18 , 365 days);
1086+ mockUSDFC.approve (address (payments), 100e18 );
1087+ payments.deposit (mockUSDFC, clientAddress, 100e18 );
9771088 vm.stopPrank ();
9781089
9791090 // Create data set as approved provider
@@ -1157,9 +1268,9 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
11571268
11581269 // Setup client payment approval if not already done
11591270 vm.startPrank (clientAddress);
1160- payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e6 , 1000e6 , 365 days);
1161- mockUSDFC.approve (address (payments), 100e6 );
1162- payments.deposit (mockUSDFC, clientAddress, 100e6 );
1271+ payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e18 , 1000e18 , 365 days);
1272+ mockUSDFC.approve (address (payments), 100e18 );
1273+ payments.deposit (mockUSDFC, clientAddress, 100e18 );
11631274 vm.stopPrank ();
11641275
11651276 // Create data set as approved provider
@@ -1302,11 +1413,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
13021413 mockUSDFC,
13031414 address (pdpServiceWithPayments),
13041415 true ,
1305- 1000e6 , // rate allowance
1306- 1000e6 , // lockup allowance
1416+ 1000e18 , // rate allowance
1417+ 1000e18 , // lockup allowance
13071418 365 days // max lockup period
13081419 );
1309- uint256 depositAmount = 100e6 ;
1420+ uint256 depositAmount = 100e18 ;
13101421 mockUSDFC.approve (address (payments), depositAmount);
13111422 payments.deposit (mockUSDFC, client, depositAmount);
13121423 vm.stopPrank ();
@@ -1482,11 +1593,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
14821593 mockUSDFC,
14831594 address (pdpServiceWithPayments),
14841595 true ,
1485- 1000e6 , // rate allowance
1486- 1000e6 , // lockup allowance
1596+ 1000e18 , // rate allowance
1597+ 1000e18 , // lockup allowance
14871598 365 days // max lockup period
14881599 );
1489- uint256 depositAmount = 100e6 ;
1600+ uint256 depositAmount = 100e18 ;
14901601 mockUSDFC.approve (address (payments), depositAmount);
14911602 payments.deposit (mockUSDFC, client, depositAmount);
14921603 vm.stopPrank ();
@@ -1603,11 +1714,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
16031714 mockUSDFC,
16041715 address (pdpServiceWithPayments),
16051716 true ,
1606- 1000e6 , // rate allowance
1607- 1000e6 , // lockup allowance
1717+ 1000e18 , // rate allowance
1718+ 1000e18 , // lockup allowance
16081719 365 days // max lockup period
16091720 );
1610- uint256 depositAmount = 100e6 ;
1721+ uint256 depositAmount = 100e18 ;
16111722 mockUSDFC.approve (address (payments), depositAmount);
16121723 payments.deposit (mockUSDFC, client, depositAmount);
16131724 vm.stopPrank ();
@@ -2822,8 +2933,8 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
28222933 );
28232934
28242935 vm.startPrank (client);
2825- payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e6 , 1000e6 , 365 days);
2826- uint256 depositAmount = 1e6 ;
2936+ payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e18 , 1000e18 , 365 days);
2937+ uint256 depositAmount = 1e18 ;
28272938 mockUSDFC.approve (address (payments), depositAmount);
28282939 payments.deposit (mockUSDFC, client, depositAmount);
28292940 vm.stopPrank ();
@@ -2875,8 +2986,8 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
28752986 );
28762987
28772988 vm.startPrank (client);
2878- payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e6 , 1000e6 , 365 days);
2879- uint256 depositAmount = 1e6 ;
2989+ payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e18 , 1000e18 , 365 days);
2990+ uint256 depositAmount = 1e18 ;
28802991 mockUSDFC.approve (address (payments), depositAmount);
28812992 payments.deposit (mockUSDFC, client, depositAmount);
28822993 vm.stopPrank ();
@@ -2926,8 +3037,8 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
29263037 );
29273038
29283039 vm.startPrank (client);
2929- payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e6 , 1000e6 , 365 days);
2930- uint256 depositAmount = 10e6 ;
3040+ payments.setOperatorApproval (mockUSDFC, address (pdpServiceWithPayments), true , 1000e18 , 1000e18 , 365 days);
3041+ uint256 depositAmount = 10e18 ;
29313042 mockUSDFC.approve (address (payments), depositAmount);
29323043 payments.deposit (mockUSDFC, client, depositAmount);
29333044 vm.stopPrank ();
@@ -4197,7 +4308,7 @@ contract FilecoinWarmStorageServiceSignatureTest is Test {
41974308 pdpService = SignatureCheckingService (address (serviceProxy));
41984309
41994310 // Fund the payer
4200- mockUSDFC.safeTransfer (payer, 1000 * 10 ** 6 ); // 1000 USDFC
4311+ mockUSDFC.safeTransfer (payer, 1000 * 10 ** 18 ); // 1000 USDFC
42014312 }
42024313
42034314 // Test the recoverSigner function indirectly through signature verification
0 commit comments