Skip to content

Commit 517cfa3

Browse files
committed
feat(pricing): make pricing constants mutable with owner-controlled updates
- Convert STORAGE_PRICE_PER_TIB_PER_MONTH, CACHE_MISS_PRICE_PER_TIB_PER_MONTH, and CDN_PRICE_PER_TIB_PER_MONTH from immutable to storage variables - Add updatePricing() owner-only function to update pricing rates - Add getCurrentPricingRates() getter function - Add PricingUpdated event to track pricing changes
1 parent 402d1a4 commit 517cfa3

File tree

2 files changed

+84
-13
lines changed

2 files changed

+84
-13
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2323
- Use `getDataSetStatusDetails()` to check termination status separately from Active/Inactive status
2424
- Subgraph schema updated with status enum and history tracking
2525
- **Calibnet**: Reduced DEFAULT_CHALLENGE_WINDOW_SIZE from 30 epochs to 20 epochs for faster testing iteration
26+
- Made storage pricing and minimum rate mutable storage variables instead of immutable constants ([#306](https://github.com/FilOzone/filecoin-services/issues/306))
27+
- `storagePricePerTibPerMonth` (initially 2.5 USDFC, max 10 USDFC)
28+
- `minimumStorageRatePerMonth` (initially 0.06 USDFC, max 0.24 USDFC)
29+
- Added `updatePricing(uint256 newStoragePrice, uint256 newMinimumRate)` function to allow owner to update pricing rates without contract upgrades
30+
- Pass 0 to keep existing value unchanged
31+
- At least one price must be non-zero
32+
- Validates against 4x upper bounds (10 USDFC storage, 0.24 USDFC minimum rate)
33+
- Price updates apply immediately to existing payment rails when recalculated
34+
- Added `getCurrentPricingRates()` function to query current storage price and minimum rate
35+
- Added `PricingUpdated(uint256 storagePrice, uint256 minimumRate)` event to track pricing changes
36+
- Added `AtLeastOnePriceMustBeNonZero` and `PriceExceedsMaximum` errors for pricing validation
2637

2738
## [0.3.0] - 2025-10-08 - M3.1 Calibration Network Deployment
2839

service_contracts/src/FilecoinWarmStorageService.sol

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ contract FilecoinWarmStorageService is
9797
event ProviderApproved(uint256 indexed providerId);
9898
event ProviderUnapproved(uint256 indexed providerId);
9999

100+
event PricingUpdated(uint256 storagePrice, uint256 minimumRate);
101+
100102
// =========================================================================
101103
// Structs
102104

@@ -193,16 +195,18 @@ contract FilecoinWarmStorageService is
193195
bytes32 private constant WITH_CDN_STRING_STORAGE_REPR =
194196
0x7769746843444e0000000000000000000000000000000000000000000000000e;
195197

196-
// Pricing constants
197-
uint256 private immutable STORAGE_PRICE_PER_TIB_PER_MONTH; // 2.5 USDFC per TiB per month without CDN with correct decimals
198+
// Pricing constants (CDN egress pricing is immutable)
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
200-
uint256 private immutable MINIMUM_STORAGE_RATE_PER_MONTH; // 0.06 USDFC per month minimum pricing floor
201201

202202
// Fixed lockup amounts for CDN rails
203203
uint256 private immutable DEFAULT_CDN_LOCKUP_AMOUNT; // 0.7 USDFC
204204
uint256 private immutable DEFAULT_CACHE_MISS_LOCKUP_AMOUNT; // 0.3 USDFC
205205

206+
// Maximum pricing bounds (4x initial values)
207+
uint256 private immutable MAX_STORAGE_PRICE_PER_TIB_PER_MONTH; // 10 USDFC (4x 2.5)
208+
uint256 private immutable MAX_MINIMUM_STORAGE_RATE_PER_MONTH; // 0.24 USDFC (4x 0.06)
209+
206210
// Token decimals
207211
uint8 private immutable TOKEN_DECIMALS;
208212

@@ -272,6 +276,10 @@ contract FilecoinWarmStorageService is
272276

273277
PlannedUpgrade private nextUpgrade;
274278

279+
// Pricing rates (mutable for future adjustments)
280+
uint256 private storagePricePerTibPerMonth;
281+
uint256 private minimumStorageRatePerMonth;
282+
275283
event UpgradeAnnounced(PlannedUpgrade plannedUpgrade);
276284

277285
// =========================================================================
@@ -328,11 +336,13 @@ contract FilecoinWarmStorageService is
328336
// Read token decimals from the USDFC token contract
329337
TOKEN_DECIMALS = _usdfc.decimals();
330338

331-
// Initialize the fee constants based on the actual token decimals
332-
STORAGE_PRICE_PER_TIB_PER_MONTH = (5 * 10 ** TOKEN_DECIMALS) / 2; // 2.5 USDFC
339+
// Initialize the immutable pricing constants based on the actual token decimals
333340
CDN_EGRESS_PRICE_PER_TIB = 7 * 10 ** TOKEN_DECIMALS; // 7 USDFC per TiB
334341
CACHE_MISS_EGRESS_PRICE_PER_TIB = 7 * 10 ** TOKEN_DECIMALS; // 7 USDFC per TiB
335-
MINIMUM_STORAGE_RATE_PER_MONTH = (6 * 10 ** TOKEN_DECIMALS) / 100; // 0.06 USDFC minimum
342+
343+
// Initialize maximum pricing bounds (4x initial values)
344+
MAX_STORAGE_PRICE_PER_TIB_PER_MONTH = 10 * 10 ** TOKEN_DECIMALS; // 10 USDFC (4x 2.5)
345+
MAX_MINIMUM_STORAGE_RATE_PER_MONTH = (24 * 10 ** TOKEN_DECIMALS) / 100; // 0.24 USDFC (4x 0.06)
336346

337347
// Initialize the lockup constants based on the actual token decimals
338348
DEFAULT_CDN_LOCKUP_AMOUNT = (7 * 10 ** TOKEN_DECIMALS) / 10; // 0.7 USDFC
@@ -383,6 +393,10 @@ contract FilecoinWarmStorageService is
383393

384394
// Set commission rate
385395
serviceCommissionBps = 0; // 0%
396+
397+
// Initialize mutable pricing variables
398+
storagePricePerTibPerMonth = (5 * 10 ** TOKEN_DECIMALS) / 2; // 2.5 USDFC
399+
minimumStorageRatePerMonth = (6 * 10 ** TOKEN_DECIMALS) / 100; // 0.06 USDFC
386400
}
387401

388402
function announcePlannedUpgrade(PlannedUpgrade calldata plannedUpgrade) external onlyOwner {
@@ -467,6 +481,39 @@ contract FilecoinWarmStorageService is
467481
serviceCommissionBps = newCommissionBps;
468482
}
469483

484+
/**
485+
* @notice Updates the pricing rates for storage services
486+
* @dev Only callable by the contract owner. Pass 0 to keep existing value unchanged.
487+
* Price updates apply immediately to existing payment rails when they're recalculated
488+
* (e.g., during piece additions/deletions or proving period updates).
489+
* Maximum allowed values: 10 USDFC for storage, 0.24 USDFC for minimum rate.
490+
* @param newStoragePrice New storage price per TiB per month (0 = no change, max 10 USDFC)
491+
* @param newMinimumRate New minimum monthly storage rate (0 = no change, max 0.24 USDFC)
492+
*/
493+
function updatePricing(uint256 newStoragePrice, uint256 newMinimumRate)
494+
external
495+
onlyOwner
496+
{
497+
if (newStoragePrice == 0 && newMinimumRate == 0) {
498+
revert Errors.AtLeastOnePriceMustBeNonZero();
499+
}
500+
501+
if (newStoragePrice > 0) {
502+
if (newStoragePrice > MAX_STORAGE_PRICE_PER_TIB_PER_MONTH) {
503+
revert Errors.PriceExceedsMaximum("storage", MAX_STORAGE_PRICE_PER_TIB_PER_MONTH, newStoragePrice);
504+
}
505+
storagePricePerTibPerMonth = newStoragePrice;
506+
}
507+
if (newMinimumRate > 0) {
508+
if (newMinimumRate > MAX_MINIMUM_STORAGE_RATE_PER_MONTH) {
509+
revert Errors.PriceExceedsMaximum("minimumRate", MAX_MINIMUM_STORAGE_RATE_PER_MONTH, newMinimumRate);
510+
}
511+
minimumStorageRatePerMonth = newMinimumRate;
512+
}
513+
514+
emit PricingUpdated(storagePricePerTibPerMonth, minimumStorageRatePerMonth);
515+
}
516+
470517
/**
471518
* @notice Adds a provider ID to the approved list
472519
* @dev Only callable by the contract owner. Reverts if already approved.
@@ -1097,7 +1144,7 @@ contract FilecoinWarmStorageService is
10971144
/// @param payer The address of the payer
10981145
function validatePayerOperatorApprovalAndFunds(FilecoinPayV1 payments, address payer) internal view {
10991146
// Calculate required lockup for minimum pricing
1100-
uint256 minimumLockupRequired = (MINIMUM_STORAGE_RATE_PER_MONTH * DEFAULT_LOCKUP_PERIOD) / EPOCHS_PER_MONTH;
1147+
uint256 minimumLockupRequired = (minimumStorageRatePerMonth * DEFAULT_LOCKUP_PERIOD) / EPOCHS_PER_MONTH;
11011148

11021149
// Check that payer has sufficient available funds
11031150
(,, uint256 availableFunds,) = payments.getAccountInfoIfSettled(usdfcTokenAddress, payer);
@@ -1120,7 +1167,7 @@ contract FilecoinWarmStorageService is
11201167
require(isApproved, Errors.OperatorNotApproved(payer, address(this)));
11211168

11221169
// Calculate minimum rate per epoch
1123-
uint256 minimumRatePerEpoch = MINIMUM_STORAGE_RATE_PER_MONTH / EPOCHS_PER_MONTH;
1170+
uint256 minimumRatePerEpoch = minimumStorageRatePerMonth / EPOCHS_PER_MONTH;
11241171

11251172
// Verify rate allowance is sufficient
11261173
require(
@@ -1241,10 +1288,10 @@ contract FilecoinWarmStorageService is
12411288
*/
12421289
function _calculateStorageRate(uint256 totalBytes) internal view returns (uint256) {
12431290
// Calculate natural size-based rate
1244-
uint256 naturalRate = calculateStorageSizeBasedRatePerEpoch(totalBytes, STORAGE_PRICE_PER_TIB_PER_MONTH);
1291+
uint256 naturalRate = calculateStorageSizeBasedRatePerEpoch(totalBytes, storagePricePerTibPerMonth);
12451292

12461293
// Calculate minimum rate (floor price converted to per-epoch)
1247-
uint256 minimumRate = MINIMUM_STORAGE_RATE_PER_MONTH / EPOCHS_PER_MONTH;
1294+
uint256 minimumRate = minimumStorageRatePerMonth / EPOCHS_PER_MONTH;
12481295

12491296
// Return whichever is higher: natural rate or minimum rate
12501297
return naturalRate > minimumRate ? naturalRate : minimumRate;
@@ -1341,22 +1388,35 @@ contract FilecoinWarmStorageService is
13411388
*/
13421389
function getServicePrice() external view returns (ServicePricing memory pricing) {
13431390
pricing = ServicePricing({
1344-
pricePerTiBPerMonthNoCDN: STORAGE_PRICE_PER_TIB_PER_MONTH,
1391+
pricePerTiBPerMonthNoCDN: storagePricePerTibPerMonth,
13451392
pricePerTiBCdnEgress: CDN_EGRESS_PRICE_PER_TIB,
13461393
pricePerTiBCacheMissEgress: CACHE_MISS_EGRESS_PRICE_PER_TIB,
13471394
tokenAddress: usdfcTokenAddress,
13481395
epochsPerMonth: EPOCHS_PER_MONTH,
1349-
minimumPricePerMonth: MINIMUM_STORAGE_RATE_PER_MONTH
1396+
minimumPricePerMonth: minimumStorageRatePerMonth
13501397
});
13511398
}
13521399

1400+
/**
1401+
* @notice Get the current pricing rates
1402+
* @return storagePrice Current storage price per TiB per month
1403+
* @return minimumRate Current minimum monthly storage rate
1404+
*/
1405+
function getCurrentPricingRates()
1406+
external
1407+
view
1408+
returns (uint256 storagePrice, uint256 minimumRate)
1409+
{
1410+
return (storagePricePerTibPerMonth, minimumStorageRatePerMonth);
1411+
}
1412+
13531413
/**
13541414
* @notice Get the effective rates after commission for both service types
13551415
* @return serviceFee Service fee (per TiB per month)
13561416
* @return spPayment SP payment (per TiB per month)
13571417
*/
13581418
function getEffectiveRates() external view returns (uint256 serviceFee, uint256 spPayment) {
1359-
uint256 total = STORAGE_PRICE_PER_TIB_PER_MONTH;
1419+
uint256 total = storagePricePerTibPerMonth;
13601420

13611421
serviceFee = (total * serviceCommissionBps) / COMMISSION_MAX_BPS;
13621422
spPayment = total - serviceFee;

0 commit comments

Comments
 (0)