@@ -159,6 +159,7 @@ contract FilecoinWarmStorageService is
159159 uint256 pricePerTiBCacheMissEgress; // Cache miss egress price per TiB (usage-based)
160160 IERC20 tokenAddress; // Address of the USDFC token
161161 uint256 epochsPerMonth; // Number of epochs in a month
162+ uint256 minimumPricePerMonth; // Minimum monthly charge for any dataset size (0.06 USDFC)
162163 }
163164
164165 // Used for announcing upgrades, packed into one slot
@@ -197,6 +198,7 @@ contract FilecoinWarmStorageService is
197198 uint256 private immutable STORAGE_PRICE_PER_TIB_PER_MONTH; // 2.5 USDFC per TiB per month without CDN with correct decimals
198199 uint256 private immutable CDN_EGRESS_PRICE_PER_TIB; // 7 USDFC per TiB of CDN egress
199200 uint256 private immutable CACHE_MISS_EGRESS_PRICE_PER_TIB; // 7 USDFC per TiB of cache miss egress
201+ uint256 private immutable MINIMUM_STORAGE_RATE_PER_MONTH; // 0.06 USDFC per month minimum pricing floor
200202
201203 // Fixed lockup amounts for CDN rails
202204 uint256 private immutable DEFAULT_CDN_LOCKUP_AMOUNT; // 0.7 USDFC
@@ -331,6 +333,7 @@ contract FilecoinWarmStorageService is
331333 STORAGE_PRICE_PER_TIB_PER_MONTH = (5 * 10 ** TOKEN_DECIMALS) / 2 ; // 2.5 USDFC
332334 CDN_EGRESS_PRICE_PER_TIB = 7 * 10 ** TOKEN_DECIMALS; // 7 USDFC per TiB
333335 CACHE_MISS_EGRESS_PRICE_PER_TIB = 7 * 10 ** TOKEN_DECIMALS; // 7 USDFC per TiB
336+ MINIMUM_STORAGE_RATE_PER_MONTH = (6 * 10 ** TOKEN_DECIMALS) / 100 ; // 0.06 USDFC minimum
334337
335338 // Initialize the lockup constants based on the actual token decimals
336339 DEFAULT_CDN_LOCKUP_AMOUNT = (7 * 10 ** TOKEN_DECIMALS) / 10 ; // 0.7 USDFC
@@ -584,6 +587,10 @@ contract FilecoinWarmStorageService is
584587
585588 // Create the payment rails using the FilecoinPayV1 contract
586589 FilecoinPayV1 payments = FilecoinPayV1 (paymentsContractAddress);
590+
591+ // Validate payer has sufficient funds and operator approvals for minimum pricing
592+ validatePayerOperatorApprovalAndFunds (payments, createData.payer);
593+
587594 uint256 pdpRailId = payments.createRail (
588595 usdfcTokenAddress, // token address
589596 createData.payer, // from (payer)
@@ -1096,15 +1103,65 @@ contract FilecoinWarmStorageService is
10961103 }
10971104 }
10981105
1106+ /// @notice Validates that the payer has sufficient funds and operator approvals for minimum pricing
1107+ /// @param payments The FilecoinPayV1 contract instance
1108+ /// @param payer The address of the payer
1109+ function validatePayerOperatorApprovalAndFunds (FilecoinPayV1 payments , address payer ) internal view {
1110+ // Calculate required lockup for minimum pricing
1111+ uint256 minimumLockupRequired = (MINIMUM_STORAGE_RATE_PER_MONTH * DEFAULT_LOCKUP_PERIOD) / EPOCHS_PER_MONTH;
1112+
1113+ // Check that payer has sufficient available funds
1114+ (,, uint256 availableFunds ,) = payments.getAccountInfoIfSettled (usdfcTokenAddress, payer);
1115+ require (
1116+ availableFunds >= minimumLockupRequired,
1117+ Errors.InsufficientFundsForMinimumRate (payer, minimumLockupRequired, availableFunds)
1118+ );
1119+
1120+ // Check operator approval settings
1121+ (
1122+ bool isApproved ,
1123+ uint256 rateAllowance ,
1124+ uint256 lockupAllowance ,
1125+ uint256 rateUsage ,
1126+ uint256 lockupUsage ,
1127+ uint256 maxLockupPeriod
1128+ ) = payments.operatorApprovals (usdfcTokenAddress, payer, address (this ));
1129+
1130+ // Verify operator is approved
1131+ require (isApproved, Errors.OperatorNotApproved (payer, address (this )));
1132+
1133+ // Calculate minimum rate per epoch
1134+ uint256 minimumRatePerEpoch = MINIMUM_STORAGE_RATE_PER_MONTH / EPOCHS_PER_MONTH;
1135+
1136+ // Verify rate allowance is sufficient
1137+ require (
1138+ rateAllowance >= rateUsage + minimumRatePerEpoch,
1139+ Errors.InsufficientRateAllowance (payer, address (this ), rateAllowance, rateUsage, minimumRatePerEpoch)
1140+ );
1141+
1142+ // Verify lockup allowance is sufficient
1143+ require (
1144+ lockupAllowance >= lockupUsage + minimumLockupRequired,
1145+ Errors.InsufficientLockupAllowance (
1146+ payer, address (this ), lockupAllowance, lockupUsage, minimumLockupRequired
1147+ )
1148+ );
1149+
1150+ // Verify max lockup period is sufficient
1151+ require (
1152+ maxLockupPeriod >= DEFAULT_LOCKUP_PERIOD,
1153+ Errors.InsufficientMaxLockupPeriod (payer, address (this ), maxLockupPeriod, DEFAULT_LOCKUP_PERIOD)
1154+ );
1155+ }
1156+
10991157 function updatePaymentRates (uint256 dataSetId , uint256 leafCount ) internal {
11001158 // Revert if no payment rail is configured for this data set
11011159 require (dataSetInfo[dataSetId].pdpRailId != 0 , Errors.NoPDPPaymentRail (dataSetId));
11021160
11031161 uint256 totalBytes = leafCount * BYTES_PER_LEAF;
11041162 FilecoinPayV1 payments = FilecoinPayV1 (paymentsContractAddress);
11051163
1106- // Update the PDP rail payment rate with the new rate and no one-time
1107- // payment
1164+ // Update the PDP rail payment rate with the new rate and no one-time payment
11081165 uint256 pdpRailId = dataSetInfo[dataSetId].pdpRailId;
11091166 uint256 newStorageRatePerEpoch = _calculateStorageRate (totalBytes);
11101167 payments.modifyRailPayment (
@@ -1177,21 +1234,29 @@ contract FilecoinWarmStorageService is
11771234
11781235 /**
11791236 * @notice Calculate storage rate per epoch based on total storage size
1180- * @dev Returns storage rate per TiB per month
1237+ * @dev Returns storage rate per TiB per month with minimum pricing floor applied
11811238 * @param totalBytes Total size of the stored data in bytes
11821239 * @return storageRate The PDP storage rate per epoch
11831240 */
1184- function calculateRatesPerEpoch (uint256 totalBytes ) external view returns (uint256 storageRate ) {
1185- storageRate = calculateStorageSizeBasedRatePerEpoch (totalBytes, STORAGE_PRICE_PER_TIB_PER_MONTH );
1241+ function calculateRatePerEpoch (uint256 totalBytes ) external view returns (uint256 storageRate ) {
1242+ storageRate = _calculateStorageRate (totalBytes);
11861243 }
11871244
11881245 /**
11891246 * @notice Calculate the storage rate per epoch (internal use)
1247+ * @dev Implements minimum pricing floor and returns the higher of the natural size-based rate or the minimum rate.
11901248 * @param totalBytes Total size of the stored data in bytes
11911249 * @return The storage rate per epoch
11921250 */
11931251 function _calculateStorageRate (uint256 totalBytes ) internal view returns (uint256 ) {
1194- return calculateStorageSizeBasedRatePerEpoch (totalBytes, STORAGE_PRICE_PER_TIB_PER_MONTH);
1252+ // Calculate natural size-based rate
1253+ uint256 naturalRate = calculateStorageSizeBasedRatePerEpoch (totalBytes, STORAGE_PRICE_PER_TIB_PER_MONTH);
1254+
1255+ // Calculate minimum rate (floor price converted to per-epoch)
1256+ uint256 minimumRate = MINIMUM_STORAGE_RATE_PER_MONTH / EPOCHS_PER_MONTH;
1257+
1258+ // Return whichever is higher: natural rate or minimum rate
1259+ return naturalRate > minimumRate ? naturalRate : minimumRate;
11951260 }
11961261
11971262 /**
@@ -1289,7 +1354,8 @@ contract FilecoinWarmStorageService is
12891354 pricePerTiBCdnEgress: CDN_EGRESS_PRICE_PER_TIB,
12901355 pricePerTiBCacheMissEgress: CACHE_MISS_EGRESS_PRICE_PER_TIB,
12911356 tokenAddress: usdfcTokenAddress,
1292- epochsPerMonth: EPOCHS_PER_MONTH
1357+ epochsPerMonth: EPOCHS_PER_MONTH,
1358+ minimumPricePerMonth: MINIMUM_STORAGE_RATE_PER_MONTH
12931359 });
12941360 }
12951361
0 commit comments