From 9107db402ab2c6c991d4113c4b053b852600a500 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 15 Sep 2026 10:10:10 +0200 Subject: [PATCH 1/3] refactor(storage): extract shared FWSS storage --- service_contracts/Makefile | 4 +- .../abi/FilecoinWarmStorageService.abi.json | 2 +- .../src/FilecoinWarmStorageService.sol | 4 +- .../FilecoinWarmStorageServiceStorage.sol | 96 +++++++++++++++++++ service_contracts/src/storage/README.md | 2 +- .../tools/storage_layout_snapshot.jq | 4 +- 6 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol diff --git a/service_contracts/Makefile b/service_contracts/Makefile index 4b9471dd..4ea2d022 100644 --- a/service_contracts/Makefile +++ b/service_contracts/Makefile @@ -33,12 +33,12 @@ build: forge build --via-ir # Storage layout generation -$(LAYOUT): tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol src/storage/FWSSStorage.sol +$(LAYOUT): tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol src/storage/FilecoinWarmStorageServiceStorage.sol bash -o pipefail -c 'bash tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol | forge fmt -r - > "$@.tmp"' mv $@.tmp $@ # Storage layout JSON (full metadata for upgrade safety checks) -$(LAYOUT_JSON): src/FilecoinWarmStorageService.sol src/storage/FWSSStorage.sol $(LAYOUT_SNAPSHOT_JQ) +$(LAYOUT_JSON): src/FilecoinWarmStorageService.sol src/storage/FilecoinWarmStorageServiceStorage.sol $(LAYOUT_SNAPSHOT_JQ) bash -o pipefail -c 'forge inspect --out out/storage-layout --cache-path cache/storage-layout --json $< storageLayout | jq -f $(LAYOUT_SNAPSHOT_JQ) > "$@.tmp"' mv $@.tmp $@ diff --git a/service_contracts/abi/FilecoinWarmStorageService.abi.json b/service_contracts/abi/FilecoinWarmStorageService.abi.json index 6dff5511..d7addb88 100644 --- a/service_contracts/abi/FilecoinWarmStorageService.abi.json +++ b/service_contracts/abi/FilecoinWarmStorageService.abi.json @@ -1300,7 +1300,7 @@ "name": "plannedUpgrade", "type": "tuple", "indexed": false, - "internalType": "struct FWSSStorage.PlannedUpgrade", + "internalType": "struct FilecoinWarmStorageServiceStorage.PlannedUpgrade", "components": [ { "name": "nextImplementation", diff --git a/service_contracts/src/FilecoinWarmStorageService.sol b/service_contracts/src/FilecoinWarmStorageService.sol index 3e0df52d..11fd009c 100644 --- a/service_contracts/src/FilecoinWarmStorageService.sol +++ b/service_contracts/src/FilecoinWarmStorageService.sol @@ -13,7 +13,7 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {FilecoinPayV1, IValidator} from "@fws-payments/FilecoinPayV1.sol"; -import {FWSSStorage} from "./storage/FWSSStorage.sol"; +import {FilecoinWarmStorageServiceStorage} from "./storage/FilecoinWarmStorageServiceStorage.sol"; import {Errors} from "./Errors.sol"; import {IFilecoinServiceMetadata} from "./IFilecoinServiceMetadata.sol"; @@ -82,7 +82,7 @@ contract FilecoinWarmStorageService is OwnableUpgradeable, Extsload, EIP712Upgradeable, - FWSSStorage + FilecoinWarmStorageServiceStorage { // Version tracking string public constant VERSION = "1.4.0"; diff --git a/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol b/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol new file mode 100644 index 00000000..a72f4d26 --- /dev/null +++ b/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +pragma solidity 0.8.30; + +/// @dev Legacy slots 0-23. Preserve field order, types and packing in every inheriting module. +abstract contract FilecoinWarmStorageServiceStorage { + struct DataSetInfo { + uint256 pdpRailId; // ID of the PDP payment rail + uint256 cacheMissRailId; // For CDN add-on: ID of the cache miss payment rail, which rewards the SP for serving data to the CDN when it doesn't already have it cached + uint256 cdnRailId; // For CDN add-on: ID of the CDN payment rail, which rewards the CDN for serving data to clients + address payer; // Address paying for storage + address payee; // SP's beneficiary address + address serviceProvider; // Current service provider of the dataset + uint256 commissionBps; // Commission rate for this data set (dynamic based on whether the client purchases CDN add-on) + uint256 clientDataSetId; // ClientDataSetID + uint256 pdpEndEpoch; // 0 if PDP rail are not terminated + uint256 providerId; // Provider ID from the ServiceProviderRegistry + uint96 pendingOneTimePayments; // fees accumulated since last flush via updateStorageRates + uint96 lifecycleReserveBalance; // local mirror of rail's lockupFixed; decremented on flush + } + + struct PlannedUpgrade { + // Address of the new implementation contract + address nextImplementation; + // Upgrade will not occur until at least this epoch + uint96 afterEpoch; + } + + // ========================================================================= + // Storage variables + // + // Each one of these variables is stored in its own storage slot and + // corresponds to the layout defined in + // FilecoinWarmStorageServiceLayout.sol. + // Storage layout should never change to ensure upgradability! + + // Proving period constants - set during initialization + uint64 internal maxProvingPeriod; + uint256 internal challengeWindowSize; + + // Commission rate + uint256 internal deprecatedServiceCommissionBps; + + // Track which proving periods have valid proofs with bitmap + mapping(uint256 dataSetId => mapping(uint256 periodId => uint256)) internal provenPeriods; + // Track when proving was first activated for each data set + mapping(uint256 dataSetId => uint256) internal provingActivationEpoch; + + mapping(uint256 dataSetId => uint256) internal provingDeadlines; + mapping(uint256 dataSetId => bool) internal provenThisPeriod; + + mapping(uint256 dataSetId => DataSetInfo) internal dataSetInfo; + + // Replay protection: tracks used nonces for both CreateDataSet and AddPieces operations. + // Stores packed data: upper 128 bits = cumulative piece count after AddPieces or 0 for CreateDataSet, + // lower 128 bits = dataSetId. For AddPieces, stores (firstAdded + pieceData.length) which is the + // next piece ID that would be assigned, providing historical data about dataset state after the operation. + mapping(address payer => mapping(uint256 nonce => uint256)) internal clientNonces; + + mapping(address payer => uint256[]) internal clientDataSets; + mapping(uint256 pdpRailId => uint256) internal railToDataSet; + + // dataSetId => (key => value) + mapping(uint256 dataSetId => mapping(string key => string value)) internal dataSetMetadata; + // dataSetId => array of keys + mapping(uint256 dataSetId => string[] keys) internal dataSetMetadataKeys; + // dataSetId => PieceId => (key => value) + mapping(uint256 dataSetId => mapping(uint256 pieceId => mapping(string key => string value))) internal + dataSetPieceMetadata; + // dataSetId => PieceId => array of keys + mapping(uint256 dataSetId => mapping(uint256 pieceId => string[] keys)) internal dataSetPieceMetadataKeys; + + // Approved provider list + mapping(uint256 providerId => bool) internal approvedProviders; + uint256[] internal approvedProviderIds; + + // View contract for read-only operations + // @dev For smart contract integrations, consider using FilecoinWarmStorageServiceStateLibrary + // directly instead of going through the view contract for more efficient gas usage. + address public viewContractAddress; + + // The address allowed to terminate CDN services + address internal filBeamControllerAddress; + + // Pending upgrade announcement + PlannedUpgrade internal nextUpgrade; + + // Pricing rates (mutable for future adjustments) + uint256 internal deprecatedStoragePricePerTibPerMonth; + uint256 internal deprecatedMinimumStorageRatePerMonth; + + // Piece IDs awaiting metadata cleanup; cleared each nextProvingPeriod call + mapping(uint256 dataSetId => uint256[] pieceIds) internal scheduledPieceMetadataRemovals; + + // Optional per-data-set authorizer (address(0) = default payer/session-key behavior). + mapping(uint256 dataSetId => address authorizer) internal dataSetAuthorizer; +} diff --git a/service_contracts/src/storage/README.md b/service_contracts/src/storage/README.md index 11fdcbbb..10625e71 100644 --- a/service_contracts/src/storage/README.md +++ b/service_contracts/src/storage/README.md @@ -1,6 +1,6 @@ # FWSS shared storage -`FWSSStorage` declares the complete legacy application layout, +`FilecoinWarmStorageServiceStorage` declares the complete legacy application layout, including retired fields and stored structs. FWSS inherits it and uses the same field names. Previously private fields become internal to allow inherited access. diff --git a/service_contracts/tools/storage_layout_snapshot.jq b/service_contracts/tools/storage_layout_snapshot.jq index 72b61d54..ccf21d70 100644 --- a/service_contracts/tools/storage_layout_snapshot.jq +++ b/service_contracts/tools/storage_layout_snapshot.jq @@ -1,8 +1,8 @@ # These two types moved to the shared storage base without changing their representation. # Keep snapshot names stable; slots, offsets, widths and recursive member types still compare exactly. def stable_type_label: - gsub("FWSSStorage\\.DataSetInfo"; "FilecoinWarmStorageService.DataSetInfo") - | gsub("FWSSStorage\\.PlannedUpgrade"; "FilecoinWarmStorageService.PlannedUpgrade"); + gsub("FilecoinWarmStorageServiceStorage\\.DataSetInfo"; "FilecoinWarmStorageService.DataSetInfo") + | gsub("FilecoinWarmStorageServiceStorage\\.PlannedUpgrade"; "FilecoinWarmStorageService.PlannedUpgrade"); def type_shape($types; $id): ($types[$id] // {label: $id}) as $type From ff494e87ba6a4d49aaf2dd984f3510fb64737107 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 16 Sep 2026 08:11:49 +0200 Subject: [PATCH 2/3] refactor(storage): rename shared storage contract --- service_contracts/Makefile | 4 +- .../abi/FilecoinWarmStorageService.abi.json | 2 +- .../src/FilecoinWarmStorageService.sol | 4 +- .../FilecoinWarmStorageServiceStorage.sol | 96 ------------------- service_contracts/src/storage/README.md | 2 +- .../tools/storage_layout_snapshot.jq | 4 +- 6 files changed, 8 insertions(+), 104 deletions(-) delete mode 100644 service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol diff --git a/service_contracts/Makefile b/service_contracts/Makefile index 4ea2d022..4b9471dd 100644 --- a/service_contracts/Makefile +++ b/service_contracts/Makefile @@ -33,12 +33,12 @@ build: forge build --via-ir # Storage layout generation -$(LAYOUT): tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol src/storage/FilecoinWarmStorageServiceStorage.sol +$(LAYOUT): tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol src/storage/FWSSStorage.sol bash -o pipefail -c 'bash tools/generate_storage_layout.sh src/FilecoinWarmStorageService.sol | forge fmt -r - > "$@.tmp"' mv $@.tmp $@ # Storage layout JSON (full metadata for upgrade safety checks) -$(LAYOUT_JSON): src/FilecoinWarmStorageService.sol src/storage/FilecoinWarmStorageServiceStorage.sol $(LAYOUT_SNAPSHOT_JQ) +$(LAYOUT_JSON): src/FilecoinWarmStorageService.sol src/storage/FWSSStorage.sol $(LAYOUT_SNAPSHOT_JQ) bash -o pipefail -c 'forge inspect --out out/storage-layout --cache-path cache/storage-layout --json $< storageLayout | jq -f $(LAYOUT_SNAPSHOT_JQ) > "$@.tmp"' mv $@.tmp $@ diff --git a/service_contracts/abi/FilecoinWarmStorageService.abi.json b/service_contracts/abi/FilecoinWarmStorageService.abi.json index d7addb88..6dff5511 100644 --- a/service_contracts/abi/FilecoinWarmStorageService.abi.json +++ b/service_contracts/abi/FilecoinWarmStorageService.abi.json @@ -1300,7 +1300,7 @@ "name": "plannedUpgrade", "type": "tuple", "indexed": false, - "internalType": "struct FilecoinWarmStorageServiceStorage.PlannedUpgrade", + "internalType": "struct FWSSStorage.PlannedUpgrade", "components": [ { "name": "nextImplementation", diff --git a/service_contracts/src/FilecoinWarmStorageService.sol b/service_contracts/src/FilecoinWarmStorageService.sol index 11fd009c..3e0df52d 100644 --- a/service_contracts/src/FilecoinWarmStorageService.sol +++ b/service_contracts/src/FilecoinWarmStorageService.sol @@ -13,7 +13,7 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {FilecoinPayV1, IValidator} from "@fws-payments/FilecoinPayV1.sol"; -import {FilecoinWarmStorageServiceStorage} from "./storage/FilecoinWarmStorageServiceStorage.sol"; +import {FWSSStorage} from "./storage/FWSSStorage.sol"; import {Errors} from "./Errors.sol"; import {IFilecoinServiceMetadata} from "./IFilecoinServiceMetadata.sol"; @@ -82,7 +82,7 @@ contract FilecoinWarmStorageService is OwnableUpgradeable, Extsload, EIP712Upgradeable, - FilecoinWarmStorageServiceStorage + FWSSStorage { // Version tracking string public constant VERSION = "1.4.0"; diff --git a/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol b/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol deleted file mode 100644 index a72f4d26..00000000 --- a/service_contracts/src/storage/FilecoinWarmStorageServiceStorage.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -pragma solidity 0.8.30; - -/// @dev Legacy slots 0-23. Preserve field order, types and packing in every inheriting module. -abstract contract FilecoinWarmStorageServiceStorage { - struct DataSetInfo { - uint256 pdpRailId; // ID of the PDP payment rail - uint256 cacheMissRailId; // For CDN add-on: ID of the cache miss payment rail, which rewards the SP for serving data to the CDN when it doesn't already have it cached - uint256 cdnRailId; // For CDN add-on: ID of the CDN payment rail, which rewards the CDN for serving data to clients - address payer; // Address paying for storage - address payee; // SP's beneficiary address - address serviceProvider; // Current service provider of the dataset - uint256 commissionBps; // Commission rate for this data set (dynamic based on whether the client purchases CDN add-on) - uint256 clientDataSetId; // ClientDataSetID - uint256 pdpEndEpoch; // 0 if PDP rail are not terminated - uint256 providerId; // Provider ID from the ServiceProviderRegistry - uint96 pendingOneTimePayments; // fees accumulated since last flush via updateStorageRates - uint96 lifecycleReserveBalance; // local mirror of rail's lockupFixed; decremented on flush - } - - struct PlannedUpgrade { - // Address of the new implementation contract - address nextImplementation; - // Upgrade will not occur until at least this epoch - uint96 afterEpoch; - } - - // ========================================================================= - // Storage variables - // - // Each one of these variables is stored in its own storage slot and - // corresponds to the layout defined in - // FilecoinWarmStorageServiceLayout.sol. - // Storage layout should never change to ensure upgradability! - - // Proving period constants - set during initialization - uint64 internal maxProvingPeriod; - uint256 internal challengeWindowSize; - - // Commission rate - uint256 internal deprecatedServiceCommissionBps; - - // Track which proving periods have valid proofs with bitmap - mapping(uint256 dataSetId => mapping(uint256 periodId => uint256)) internal provenPeriods; - // Track when proving was first activated for each data set - mapping(uint256 dataSetId => uint256) internal provingActivationEpoch; - - mapping(uint256 dataSetId => uint256) internal provingDeadlines; - mapping(uint256 dataSetId => bool) internal provenThisPeriod; - - mapping(uint256 dataSetId => DataSetInfo) internal dataSetInfo; - - // Replay protection: tracks used nonces for both CreateDataSet and AddPieces operations. - // Stores packed data: upper 128 bits = cumulative piece count after AddPieces or 0 for CreateDataSet, - // lower 128 bits = dataSetId. For AddPieces, stores (firstAdded + pieceData.length) which is the - // next piece ID that would be assigned, providing historical data about dataset state after the operation. - mapping(address payer => mapping(uint256 nonce => uint256)) internal clientNonces; - - mapping(address payer => uint256[]) internal clientDataSets; - mapping(uint256 pdpRailId => uint256) internal railToDataSet; - - // dataSetId => (key => value) - mapping(uint256 dataSetId => mapping(string key => string value)) internal dataSetMetadata; - // dataSetId => array of keys - mapping(uint256 dataSetId => string[] keys) internal dataSetMetadataKeys; - // dataSetId => PieceId => (key => value) - mapping(uint256 dataSetId => mapping(uint256 pieceId => mapping(string key => string value))) internal - dataSetPieceMetadata; - // dataSetId => PieceId => array of keys - mapping(uint256 dataSetId => mapping(uint256 pieceId => string[] keys)) internal dataSetPieceMetadataKeys; - - // Approved provider list - mapping(uint256 providerId => bool) internal approvedProviders; - uint256[] internal approvedProviderIds; - - // View contract for read-only operations - // @dev For smart contract integrations, consider using FilecoinWarmStorageServiceStateLibrary - // directly instead of going through the view contract for more efficient gas usage. - address public viewContractAddress; - - // The address allowed to terminate CDN services - address internal filBeamControllerAddress; - - // Pending upgrade announcement - PlannedUpgrade internal nextUpgrade; - - // Pricing rates (mutable for future adjustments) - uint256 internal deprecatedStoragePricePerTibPerMonth; - uint256 internal deprecatedMinimumStorageRatePerMonth; - - // Piece IDs awaiting metadata cleanup; cleared each nextProvingPeriod call - mapping(uint256 dataSetId => uint256[] pieceIds) internal scheduledPieceMetadataRemovals; - - // Optional per-data-set authorizer (address(0) = default payer/session-key behavior). - mapping(uint256 dataSetId => address authorizer) internal dataSetAuthorizer; -} diff --git a/service_contracts/src/storage/README.md b/service_contracts/src/storage/README.md index 10625e71..11fdcbbb 100644 --- a/service_contracts/src/storage/README.md +++ b/service_contracts/src/storage/README.md @@ -1,6 +1,6 @@ # FWSS shared storage -`FilecoinWarmStorageServiceStorage` declares the complete legacy application layout, +`FWSSStorage` declares the complete legacy application layout, including retired fields and stored structs. FWSS inherits it and uses the same field names. Previously private fields become internal to allow inherited access. diff --git a/service_contracts/tools/storage_layout_snapshot.jq b/service_contracts/tools/storage_layout_snapshot.jq index ccf21d70..72b61d54 100644 --- a/service_contracts/tools/storage_layout_snapshot.jq +++ b/service_contracts/tools/storage_layout_snapshot.jq @@ -1,8 +1,8 @@ # These two types moved to the shared storage base without changing their representation. # Keep snapshot names stable; slots, offsets, widths and recursive member types still compare exactly. def stable_type_label: - gsub("FilecoinWarmStorageServiceStorage\\.DataSetInfo"; "FilecoinWarmStorageService.DataSetInfo") - | gsub("FilecoinWarmStorageServiceStorage\\.PlannedUpgrade"; "FilecoinWarmStorageService.PlannedUpgrade"); + gsub("FWSSStorage\\.DataSetInfo"; "FilecoinWarmStorageService.DataSetInfo") + | gsub("FWSSStorage\\.PlannedUpgrade"; "FilecoinWarmStorageService.PlannedUpgrade"); def type_shape($types; $id): ($types[$id] // {label: $id}) as $type From ee3c5a574db17930171cdd4b4101f807a447a09b Mon Sep 17 00:00:00 2001 From: Filip-L Date: Thu, 17 Sep 2026 15:38:41 +0200 Subject: [PATCH 3/3] Add metadata module --- service_contracts/Makefile | 1 + ...nWarmStorageServiceMetadataModule.abi.json | 54 +++++++++++++++++++ .../src/FilecoinWarmStorageService.sol | 23 +------- ...lecoinWarmStorageServiceMetadataModule.sol | 27 ++++++++++ .../test/FilecoinWarmStorageService.t.sol | 17 ------ ...coinWarmStorageServiceMetadataModule.t.sol | 31 +++++++++++ 6 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 service_contracts/abi/FilecoinWarmStorageServiceMetadataModule.abi.json create mode 100644 service_contracts/src/modules/FilecoinWarmStorageServiceMetadataModule.sol create mode 100644 service_contracts/test/modules/FilecoinWarmStorageServiceMetadataModule.t.sol diff --git a/service_contracts/Makefile b/service_contracts/Makefile index 4b9471dd..cde337dd 100644 --- a/service_contracts/Makefile +++ b/service_contracts/Makefile @@ -141,6 +141,7 @@ check-layout: # Core contracts we publish ABIs for. ABI_CONTRACTS := \ FilecoinWarmStorageService \ + FilecoinWarmStorageServiceMetadataModule \ FilecoinWarmStorageServiceProviderManagementModule \ IFilecoinServiceMetadata \ FilecoinWarmStorageServiceStateLibrary \ diff --git a/service_contracts/abi/FilecoinWarmStorageServiceMetadataModule.abi.json b/service_contracts/abi/FilecoinWarmStorageServiceMetadataModule.abi.json new file mode 100644 index 00000000..59a1ea35 --- /dev/null +++ b/service_contracts/abi/FilecoinWarmStorageServiceMetadataModule.abi.json @@ -0,0 +1,54 @@ +[ + { + "type": "function", + "name": "VERSION", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "description", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "homepage", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "pure" + } +] diff --git a/service_contracts/src/FilecoinWarmStorageService.sol b/service_contracts/src/FilecoinWarmStorageService.sol index 3e0df52d..9f2c245d 100644 --- a/service_contracts/src/FilecoinWarmStorageService.sol +++ b/service_contracts/src/FilecoinWarmStorageService.sol @@ -13,9 +13,9 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {FilecoinPayV1, IValidator} from "@fws-payments/FilecoinPayV1.sol"; +import {FilecoinWarmStorageServiceMetadataModule} from "./modules/FilecoinWarmStorageServiceMetadataModule.sol"; import {FWSSStorage} from "./storage/FWSSStorage.sol"; import {Errors} from "./Errors.sol"; -import {IFilecoinServiceMetadata} from "./IFilecoinServiceMetadata.sol"; import {ServiceProviderRegistry} from "./ServiceProviderRegistry.sol"; @@ -74,7 +74,7 @@ uint256 constant MAX_TERMINATE_SERVICE_EXTRA_DATA_SIZE = 1024; // 1KiB /// and adjusts payment rates based on storage size. Also implements validation /// to reduce payments for faulted epochs. contract FilecoinWarmStorageService is - IFilecoinServiceMetadata, + FilecoinWarmStorageServiceMetadataModule, PDPListener, IValidator, Initializable, @@ -84,13 +84,6 @@ contract FilecoinWarmStorageService is EIP712Upgradeable, FWSSStorage { - // Version tracking - string public constant VERSION = "1.4.0"; - string private constant SERVICE_NAME = "Filecoin Warm Storage Service"; - string private constant SERVICE_DESCRIPTION = - "Warm storage service for the Filecoin Onchain Cloud. Manages PDP-backed datasets, Filecoin Pay storage rails, lifecycle fees, and optional CDN payment rails."; - string private constant SERVICE_HOMEPAGE = "https://github.com/FilOzone/filecoin-services"; - using Rails for FilecoinPayV1; // Events @@ -319,18 +312,6 @@ contract FilecoinWarmStorageService is challengeWindowSize = _challengeWindowSize; } - function name() external pure override returns (string memory) { - return SERVICE_NAME; - } - - function description() external pure override returns (string memory) { - return SERVICE_DESCRIPTION; - } - - function homepage() external pure override returns (string memory) { - return SERVICE_HOMEPAGE; - } - function announceUpgradePlan(address nextImplementation, uint96 delayEpochs) external { if (delayEpochs == 0) { delayEpochs = 1; diff --git a/service_contracts/src/modules/FilecoinWarmStorageServiceMetadataModule.sol b/service_contracts/src/modules/FilecoinWarmStorageServiceMetadataModule.sol new file mode 100644 index 00000000..249c88bc --- /dev/null +++ b/service_contracts/src/modules/FilecoinWarmStorageServiceMetadataModule.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +pragma solidity 0.8.30; + +import {IFilecoinServiceMetadata} from "../IFilecoinServiceMetadata.sol"; + +/// @title FilecoinWarmStorageServiceMetadataModule +/// @notice Exposes static FWSS service metadata. +contract FilecoinWarmStorageServiceMetadataModule is IFilecoinServiceMetadata { + // Version tracking + string public constant VERSION = "1.4.0"; + string internal constant SERVICE_NAME = "Filecoin Warm Storage Service"; + string internal constant SERVICE_DESCRIPTION = + "Warm storage service for the Filecoin Onchain Cloud. Manages PDP-backed datasets, Filecoin Pay storage rails, lifecycle fees, and optional CDN payment rails."; + string private constant SERVICE_HOMEPAGE = "https://github.com/FilOzone/filecoin-services"; + + function name() external pure override returns (string memory) { + return SERVICE_NAME; + } + + function description() external pure override returns (string memory) { + return SERVICE_DESCRIPTION; + } + + function homepage() external pure override returns (string memory) { + return SERVICE_HOMEPAGE; + } +} diff --git a/service_contracts/test/FilecoinWarmStorageService.t.sol b/service_contracts/test/FilecoinWarmStorageService.t.sol index 6833389f..e5a65aae 100644 --- a/service_contracts/test/FilecoinWarmStorageService.t.sol +++ b/service_contracts/test/FilecoinWarmStorageService.t.sol @@ -565,23 +565,6 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest { assertTrue(found, "FilecoinServiceDeployed event should be emitted"); } - function testServiceMetadata() public view { - IFilecoinServiceMetadata metadata = IFilecoinServiceMetadata(address(pdpServiceWithPayments)); - string memory serviceName = metadata.name(); - string memory serviceDescription = metadata.description(); - string memory serviceHomepage = metadata.homepage(); - - assertEq(serviceName, "Filecoin Warm Storage Service", "Service name should match"); - assertEq( - serviceDescription, - "Warm storage service for the Filecoin Onchain Cloud. Manages PDP-backed datasets, Filecoin Pay storage rails, lifecycle fees, and optional CDN payment rails.", - "Service description should match" - ); - assertEq(serviceHomepage, "https://github.com/FilOzone/filecoin-services", "Service homepage should match"); - assertLe(bytes(serviceDescription).length, 256, "Service description should not exceed 256 bytes"); - assertLe(bytes(serviceHomepage).length, 256, "Service homepage should not exceed 256 bytes"); - } - function testAnnounceUpgradePlan() public { FilecoinWarmStorageService firstServiceImpl = new FilecoinWarmStorageService( address(mockPDPVerifier), diff --git a/service_contracts/test/modules/FilecoinWarmStorageServiceMetadataModule.t.sol b/service_contracts/test/modules/FilecoinWarmStorageServiceMetadataModule.t.sol new file mode 100644 index 00000000..a367454b --- /dev/null +++ b/service_contracts/test/modules/FilecoinWarmStorageServiceMetadataModule.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {FilecoinWarmStorageServiceMetadataModule} from "../../src/modules/FilecoinWarmStorageServiceMetadataModule.sol"; +import {IFilecoinServiceMetadata} from "../../src/IFilecoinServiceMetadata.sol"; + +contract FilecoinWarmStorageServiceMetadataModuleTest is Test { + FilecoinWarmStorageServiceMetadataModule public metadataModule; + + function setUp() public { + metadataModule = new FilecoinWarmStorageServiceMetadataModule(); + } + + function testServiceMetadata() public view { + IFilecoinServiceMetadata metadata = IFilecoinServiceMetadata(address(metadataModule)); + string memory serviceName = metadata.name(); + string memory serviceDescription = metadata.description(); + string memory serviceHomepage = metadata.homepage(); + + assertEq(serviceName, "Filecoin Warm Storage Service", "Service name should match"); + assertEq( + serviceDescription, + "Warm storage service for the Filecoin Onchain Cloud. Manages PDP-backed datasets, Filecoin Pay storage rails, lifecycle fees, and optional CDN payment rails.", + "Service description should match" + ); + assertEq(serviceHomepage, "https://github.com/FilOzone/filecoin-services", "Service homepage should match"); + assertLe(bytes(serviceDescription).length, 256, "Service description should not exceed 256 bytes"); + assertLe(bytes(serviceHomepage).length, 256, "Service homepage should not exceed 256 bytes"); + } +}