From 2cc63187048a3e166f14cf0c61986bc771b15c06 Mon Sep 17 00:00:00 2001 From: Chaitu-Tatipamula Date: Wed, 21 Jan 2026 01:13:38 +0530 Subject: [PATCH 1/4] feat: make FilBeamOperator upgradeable with UUPS proxy pattern --- DEPLOYMENT.md | 70 +++++++- README.md | 35 +++- script/DeployFilBeamOperator.s.sol | 35 +++- src/FilBeamOperator.sol | 39 ++++- test/FilBeamOperator.t.sol | 266 ++++++++++++++++++++++------- 5 files changed, 350 insertions(+), 95 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 0f37376..29e4dc3 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -9,7 +9,13 @@ ## Overview -The FilBeamOperator contract manages CDN and cache-miss usage reporting and payment settlement for the FilBeam service. This guide covers deployment and migration procedures. +The FilBeamOperator contract manages CDN and cache-miss usage reporting and payment settlement for the FilBeam service. The contract uses the **UUPS proxy pattern** for upgradeability. + +### Architecture +- **Proxy Contract**: The address users interact with (stores all state) +- **Implementation Contract**: Contains the business logic (can be upgraded) + +This guide covers deployment, upgrades, and migration procedures. ## Initial Deployment @@ -39,7 +45,7 @@ The FilBeamOperator contract manages CDN and cache-miss usage reporting and paym ### Deployment Steps -#### Step 1: Deploy New FilBeamOperator Contract +#### Step 1: Deploy FilBeamOperator (Proxy + Implementation) ```bash # Deploy the contract @@ -50,22 +56,27 @@ forge script script/DeployFilBeamOperator.s.sol \ # Expected output: # === FilBeamOperator Deployment Complete === -# FilBeamOperator deployed at: 0x... -# === Configuration === -# FWSS address: -# Payments address: +# === Contract Addresses === +# Proxy Address (use this!): 0x... <-- This is the address you use +# Implementation Address: 0x... <-- For reference only +# === Verification === +# Contract Version: 1.0.0 # ... ``` #### Step 2: Verify Deployment ```bash +# Set the PROXY address (not implementation!) +export FILBEAM_OPERATOR_ADDRESS=0x... # Use Proxy Address from deployment output + # Verify contract configuration cast call $FILBEAM_OPERATOR_ADDRESS "fwssContractAddress()" --rpc-url $RPC_URL cast call $FILBEAM_OPERATOR_ADDRESS "paymentsContractAddress()" --rpc-url $RPC_URL cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL cast call $FILBEAM_OPERATOR_ADDRESS "cacheMissRatePerByte()" --rpc-url $RPC_URL cast call $FILBEAM_OPERATOR_ADDRESS "filBeamOperatorController()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL # Should return "1.0.0" ``` #### Step 3: Transfer FWSS Controller Authorization @@ -111,9 +122,52 @@ cast call $FWSS_ADDRESS \ ## Future Contract Upgrades -### Rate Change Procedure +FilBeamOperator supports two types of upgrades: + +1. **UUPS Upgrade** - For bug fixes or feature additions (preserves rates and state) +2. **Rate Change Migration** - Requires deploying a new proxy (rates are immutable per deployment) + +### UUPS Upgrade (Bug Fixes / Features) + +Use this for logic changes that don't require modifying rates. + +#### Step 1: Deploy New Implementation + +```bash +# Deploy only the new implementation contract +forge create src/FilBeamOperator.sol:FilBeamOperator \ + --rpc-url $RPC_URL \ + --private-key $OWNER_PRIVATE_KEY + +export NEW_IMPLEMENTATION=0x... # New implementation address +``` + +#### Step 2: Upgrade the Proxy + +```bash +# Owner calls upgradeToAndCall on the proxy +cast send $FILBEAM_OPERATOR_ADDRESS \ + "upgradeToAndCall(address,bytes)" \ + $NEW_IMPLEMENTATION \ + "0x" \ + --private-key $OWNER_PRIVATE_KEY \ + --rpc-url $RPC_URL +``` + +#### Step 3: Verify Upgrade + +```bash +# Check new version +cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL +# Should return new version (e.g., "1.1.0") + +# Verify state is preserved +cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL +``` + +### Rate Change Migration -Since rates are immutable in FilBeamOperator, changing rates requires deploying a new contract. FWSS can only have one authorized FilBeamController at a time, which affects settlement capabilities. +Since rates are immutable in FilBeamOperator, changing rates requires deploying a new proxy. FWSS can only have one authorized FilBeamController at a time, which affects settlement capabilities. ### Migration Approach: Clean Transition diff --git a/README.md b/README.md index cb53d76..597ae29 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ FilBeamOperator is a smart contract used for aggregating CDN and cache-miss usag - **Usage Reporting**: Batch methods for reporting CDN and cache-miss usage - **Rail Settlements**: Independent settlement for CDN and cache-miss payment rails - **Access Control**: Separate roles for contract management and usage reporting +- **Upgradeable**: Uses UUPS proxy pattern for safe contract upgrades ## Foundry @@ -39,15 +40,20 @@ $ forge fmt For full deployment and migration guide refer to the [DEPLOYMENT](./DEPLOYMENT.md) document in this repository. -The FilBeamOperator contract requires the following constructor parameters: +The FilBeamOperator contract uses the UUPS proxy pattern. Deployment creates: +1. **Implementation Contract** - Contains the logic (don't interact with this directly) +2. **Proxy Contract** - The address users interact with (stores all state) + +The contract is initialized with the following parameters: ```solidity -constructor( +function initialize( address fwssAddress, // FWSS contract address - address _paymentsAddress, // Payments contract address for rail management - uint256 _cdnRatePerByte, // Rate per byte for CDN usage - uint256 _cacheMissRatePerByte, // Rate per byte for cache-miss usage - address _filBeamOperatorController // Address authorized to report usage + address fwssStateViewAddress, // FWSS State View contract address + address paymentsAddress, // Payments contract address for rail management + uint256 cdnRatePerByte, // Rate per byte for CDN usage + uint256 cacheMissRatePerByte, // Rate per byte for cache-miss usage + address filBeamOperatorController // Address authorized to report usage ) ``` @@ -59,6 +65,7 @@ Deploy the contract using Forge script: PRIVATE_KEY= \ FILBEAM_CONTROLLER= \ FWSS_ADDRESS= \ +FWSS_STATE_VIEW_ADDRESS= \ CDN_PRICE_USD_PER_TIB= \ CACHE_MISS_PRICE_USD_PER_TIB= \ PRICE_DECIMALS= \ @@ -67,6 +74,10 @@ forge script script/DeployFilBeamOperator.s.sol \ --broadcast ``` +The deployment will output: +- **Proxy Address** - Use this address for all interactions +- **Implementation Address** - For reference only + **Note**: The deployer address automatically becomes the contract owner. ## Contract API @@ -104,6 +115,12 @@ function transferOwnership(address newOwner) external onlyOwner function setFilBeamOperatorController(address _filBeamOperatorController) external onlyOwner ``` +**Upgrades (UUPS)** +```solidity +function upgradeToAndCall(address newImplementation, bytes memory data) external onlyOwner +function version() public pure returns (string memory) // Returns current version +``` + ## Key Concepts ### Batch Operations @@ -122,6 +139,12 @@ function setFilBeamOperatorController(address _filBeamOperatorController) extern - **Epoch-Based**: Settlement periods defined by epoch ranges - **Accumulative**: Usage accumulates between settlements +### Upgradeability (UUPS Pattern) +- **Proxy Pattern**: Users interact with a proxy that delegates to an implementation +- **State Preservation**: All data is stored in the proxy and preserved during upgrades +- **Owner-Only Upgrades**: Only the contract owner can authorize upgrades +- **Version Tracking**: Use `version()` to verify the current implementation version + ### Cast ```shell diff --git a/script/DeployFilBeamOperator.s.sol b/script/DeployFilBeamOperator.s.sol index 695fff5..7732a9c 100644 --- a/script/DeployFilBeamOperator.s.sol +++ b/script/DeployFilBeamOperator.s.sol @@ -5,6 +5,7 @@ import "forge-std/Script.sol"; import "../src/FilBeamOperator.sol"; import {FilecoinWarmStorageService} from "@filecoin-services/FilecoinWarmStorageService.sol"; import {FilecoinWarmStorageServiceStateView} from "@filecoin-services/FilecoinWarmStorageServiceStateView.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; interface IERC20 { function decimals() external view returns (uint8); @@ -62,20 +63,38 @@ contract DeployFilBeamOperator is Script { vm.startBroadcast(deployerPrivateKey); - // Deploy the FilBeamOperator contract (deployer becomes owner) - FilBeamOperator filBeam = new FilBeamOperator( - fwssAddress, - fwssStateViewAddress, - paymentsAddress, - cdnRatePerByte, - cacheMissRatePerByte, - filBeamOperatorController + // Step 1: Deploy the implementation + FilBeamOperator implementation = new FilBeamOperator(); + + // Step 2: Encode the initialize call + bytes memory initializeData = abi.encodeCall( + FilBeamOperator.initialize, + ( + fwssAddress, + fwssStateViewAddress, + paymentsAddress, + cdnRatePerByte, + cacheMissRatePerByte, + filBeamOperatorController + ) ); + //Step 3: Deploy ERC1967 proxy pointing to the implementation + ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializeData); + + // Step 4: cast the proxy to FIlBeamOperator for verification + FilBeamOperator filBeam = FilBeamOperator(address(proxy)); + vm.stopBroadcast(); // Log deployment information console2.log("=== FilBeamOperator Deployment Complete ==="); + console2.log("=== Contract Addresses ==="); + console2.log("Proxy Address (use this):", address(proxy)); + console2.log("Implementation Address:", address(implementation)); + console2.log(""); + console2.log("=== Verification ==="); + console2.log("Contract Version:", filBeam.version()); console2.log("FilBeamOperator deployed at:", address(filBeam)); console2.log(""); console2.log("=== Configuration ==="); diff --git a/src/FilBeamOperator.sol b/src/FilBeamOperator.sol index 38d3f2d..6c3a98c 100644 --- a/src/FilBeamOperator.sol +++ b/src/FilBeamOperator.sol @@ -2,23 +2,26 @@ pragma solidity ^0.8.13; import "./Errors.sol"; -import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {FilecoinPayV1} from "@filecoin-pay/FilecoinPayV1.sol"; import {FilecoinWarmStorageService} from "@filecoin-services/FilecoinWarmStorageService.sol"; import {FilecoinWarmStorageServiceStateView} from "@filecoin-services/FilecoinWarmStorageServiceStateView.sol"; -contract FilBeamOperator is Ownable2Step { +contract FilBeamOperator is Initializable, Ownable2StepUpgradeable, UUPSUpgradeable { struct DataSetUsage { uint256 cdnAmount; uint256 cacheMissAmount; uint256 maxReportedEpoch; } - address public immutable fwssContractAddress; - address public immutable fwssStateViewContractAddress; - address public immutable paymentsContractAddress; - uint256 public immutable cdnRatePerByte; - uint256 public immutable cacheMissRatePerByte; + address public fwssContractAddress; + address public fwssStateViewContractAddress; + address public paymentsContractAddress; + uint256 public cdnRatePerByte; + uint256 public cacheMissRatePerByte; address public filBeamOperatorController; mapping(uint256 => DataSetUsage) public dataSetUsage; @@ -41,6 +44,11 @@ contract FilBeamOperator is Ownable2Step { event FwssFilBeamControllerChanged(address indexed previousController, address indexed newController); + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + /// @notice Initializes the FilBeamOperator contract /// @param _fwssAddress Address of the FWSS contract /// @param _fwssStateViewAddress Address of the FWSS State View Contract @@ -48,20 +56,24 @@ contract FilBeamOperator is Ownable2Step { /// @param _cdnRatePerByte CDN rate per byte in smallest token units /// @param _cacheMissRatePerByte Cache miss rate per byte in smallest token units /// @param _filBeamOperatorController Address authorized to record usage and terminate payment rails - constructor( + function initialize( address _fwssAddress, address _fwssStateViewAddress, address _paymentsAddress, uint256 _cdnRatePerByte, uint256 _cacheMissRatePerByte, address _filBeamOperatorController - ) Ownable(msg.sender) { + ) public initializer { if (_fwssAddress == address(0)) revert InvalidAddress(); if (_fwssStateViewAddress == address(0)) revert InvalidAddress(); if (_paymentsAddress == address(0)) revert InvalidAddress(); if (_cdnRatePerByte == 0 || _cacheMissRatePerByte == 0) revert InvalidRate(); if (_filBeamOperatorController == address(0)) revert InvalidAddress(); + __Ownable_init(msg.sender); + __Ownable2Step_init(); + __UUPSUpgradeable_init(); + fwssContractAddress = _fwssAddress; fwssStateViewContractAddress = _fwssStateViewAddress; paymentsContractAddress = _paymentsAddress; @@ -229,4 +241,13 @@ contract FilBeamOperator is Ownable2Step { // Return the minimum of requested amount and available lockup return requestedAmount > rail.lockupFixed ? rail.lockupFixed : requestedAmount; } + + /// @notice Authorizes contract upgrades - only callable by owner + /// @dev Required by UUPS pattern + /// @param newImplementation Address of new implementation contract + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + + function version() public pure virtual returns (string memory) { + return "1.0.0"; + } } diff --git a/test/FilBeamOperator.t.sol b/test/FilBeamOperator.t.sol index af4df8f..81d052e 100644 --- a/test/FilBeamOperator.t.sol +++ b/test/FilBeamOperator.t.sol @@ -9,6 +9,7 @@ import {MockFilecoinWarmStorageServiceStateView} from "../src/mocks/MockFilecoin import {MockPayments} from "../src/mocks/MockPayments.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../src/Errors.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract FilBeamOperatorTest is Test { FilBeamOperator public filBeam; @@ -19,6 +20,8 @@ contract FilBeamOperatorTest is Test { address public filBeamOperatorController; address public user1; address public user2; + FilBeamOperator public implementation; + ERC1967Proxy public proxy; uint256 constant DATA_SET_ID_1 = 1; uint256 constant DATA_SET_ID_2 = 2; @@ -55,16 +58,28 @@ contract FilBeamOperatorTest is Test { mockStateView = new MockFilecoinWarmStorageServiceStateView(); mockPayments = new MockPayments(); - // Deploy FilBeamOperator contract (deployer becomes owner) - filBeam = new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + // Deploy implementation + implementation = new FilBeamOperator(); + + // Encode intialize call + bytes memory initializeData = abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) ); + // Deploy proxy + proxy = new ERC1967Proxy(address(implementation), initializeData); + + // Cast the proxy to FilBeamOperator + filBeam = FilBeamOperator(address(proxy)); + mockFWSS.setAuthorizedCaller(address(filBeam)); // Set up default rails for testing @@ -191,68 +206,110 @@ contract FilBeamOperatorTest is Test { } function test_InitializeRevertZeroAddress() public { + FilBeamOperator impl = new FilBeamOperator(); + vm.expectRevert(InvalidAddress.selector); - new FilBeamOperator( - address(0), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(0), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); vm.expectRevert(InvalidAddress.selector); - new FilBeamOperator( - address(mockFWSS), - address(0), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(0), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); vm.expectRevert(InvalidAddress.selector); - new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(0), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(0), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); } function test_InitializeRevertZeroRate() public { + FilBeamOperator impl = new FilBeamOperator(); + vm.expectRevert(InvalidRate.selector); - new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - 0, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + 0, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); vm.expectRevert(InvalidRate.selector); - new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - 0, - filBeamOperatorController + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + 0, + filBeamOperatorController + ) + ) ); } function test_InitializeRevertZeroFilBeamController() public { + FilBeamOperator impl = new FilBeamOperator(); + vm.expectRevert(InvalidAddress.selector); - new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - address(0) + new ERC1967Proxy( + address(impl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + address(0) + ) + ) ); } @@ -1407,15 +1464,25 @@ contract FilBeamOperatorTest is Test { function test_TransferFwssFilBeamController_NewOperatorCanCallAfterMigration() public { // Deploy a new FilBeamOperator instance to act as the new operator - FilBeamOperator newOperator = new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + FilBeamOperator newOperatorImpl = new FilBeamOperator(); + + ERC1967Proxy newOperatorProxy = new ERC1967Proxy( + address(newOperatorImpl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); + FilBeamOperator newOperator = FilBeamOperator(address(newOperatorProxy)); + // Record usage with old operator vm.prank(filBeamOperatorController); filBeam.recordUsageRollups( @@ -1441,15 +1508,25 @@ contract FilBeamOperatorTest is Test { function test_TransferFwssFilBeamController_IntegrationFlow() public { // Deploy new operator - FilBeamOperator newOperator = new FilBeamOperator( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + FilBeamOperator newOperatorImpl = new FilBeamOperator(); + + ERC1967Proxy newOperatorProxy = new ERC1967Proxy( + address(newOperatorImpl), + abi.encodeCall( + FilBeamOperator.initialize, + ( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ) + ) ); + FilBeamOperator newOperator = FilBeamOperator(address(newOperatorProxy)); + // 1. Old operator records usage vm.prank(filBeamOperatorController); filBeam.recordUsageRollups( @@ -1486,4 +1563,65 @@ contract FilBeamOperatorTest is Test { vm.expectRevert(MockFWSS.UnauthorizedCaller.selector); filBeam.settleCDNPaymentRails(_singleUint256Array(DATA_SET_ID_1)); } + + // ============ Upgrade Tests ============ + + function test_CannotInitializeTwice() public { + vm.expectRevert(); + filBeam.initialize( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ); + } + + function test_CannotInitializeImplementationDirectly() public { + vm.expectRevert(); + implementation.initialize( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE, + filBeamOperatorController + ); + } + + function test_OnlyOwnerCanUpgrade() public { + FilBeamOperator newImpl = new FilBeamOperator(); + + vm.prank(user1); + vm.expectRevert(); + filBeam.upgradeToAndCall(address(newImpl), ""); + } + + function test_OwnerCanUpgrade() public { + // Record some state intitally + vm.prank(filBeamOperatorController); + filBeam.recordUsageRollups( + 1, _singleUint256Array(DATA_SET_ID_1), _singleUint256Array(1000), _singleUint256Array(500) + ); + + // Get state before upgrade + (uint256 cdnBefore, uint256 cacheMissBefore, uint256 epochBefore) = filBeam.dataSetUsage(DATA_SET_ID_1); + address ownerBefore = filBeam.owner(); + + // Deploy new implementation + FilBeamOperator newImpl = new FilBeamOperator(); + filBeam.upgradeToAndCall(address(newImpl), ""); + + // Get state after upgrade + (uint256 cdnAfter, uint256 cacheMissAfter, uint256 epochAfter) = filBeam.dataSetUsage(DATA_SET_ID_1); + assertEq(cdnBefore, cdnAfter, "CDN amount should be preserved"); + assertEq(cacheMissBefore, cacheMissAfter, "Cache miss amount should be preserved"); + assertEq(epochBefore, epochAfter, "Epoch should be preserved"); + assertEq(ownerBefore, filBeam.owner(), "Owner should be preserved"); + } + + function test_Version() public view { + assertEq(filBeam.version(), "1.0.0"); + } } From d7434fc263edcd6a15c6360ab8a13dead8ea8be8 Mon Sep 17 00:00:00 2001 From: Chaitu-Tatipamula <107246959+Chaitu-Tatipamula@users.noreply.github.com> Date: Thu, 22 Jan 2026 01:24:11 +0530 Subject: [PATCH 2/4] refactor: address PR review feedback - immutable variables and documentation improvements --- DEPLOYMENT.md | 28 ++-- script/DeployFilBeamOperator.s.sol | 16 +-- script/UpgradeFilBeamOperator.s.sol | 62 +++++++++ src/FilBeamOperator.sol | 41 +++--- test/FilBeamOperator.t.sol | 194 ++++++++-------------------- 5 files changed, 156 insertions(+), 185 deletions(-) create mode 100644 script/UpgradeFilBeamOperator.s.sol diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 29e4dc3..0747dc6 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -68,15 +68,15 @@ forge script script/DeployFilBeamOperator.s.sol \ ```bash # Set the PROXY address (not implementation!) -export FILBEAM_OPERATOR_ADDRESS=0x... # Use Proxy Address from deployment output +export FILBEAM_OPERATOR_PROXY_ADDRESS=0x... # Verify contract configuration -cast call $FILBEAM_OPERATOR_ADDRESS "fwssContractAddress()" --rpc-url $RPC_URL -cast call $FILBEAM_OPERATOR_ADDRESS "paymentsContractAddress()" --rpc-url $RPC_URL -cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL -cast call $FILBEAM_OPERATOR_ADDRESS "cacheMissRatePerByte()" --rpc-url $RPC_URL -cast call $FILBEAM_OPERATOR_ADDRESS "filBeamOperatorController()" --rpc-url $RPC_URL -cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL # Should return "1.0.0" +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "fwssContractAddress()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "paymentsContractAddress()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cacheMissRatePerByte()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "filBeamOperatorController()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "version()" --rpc-url $RPC_URL # Should return "1.0.0" ``` #### Step 3: Transfer FWSS Controller Authorization @@ -85,7 +85,7 @@ cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL # Should ret # Current FWSS controller should execute this cast send $FWSS_ADDRESS \ "transferFilBeamController(address)" \ - $FILBEAM_OPERATOR_ADDRESS \ + $FILBEAM_OPERATOR_PROXY_ADDRESS \ --private-key $CURRENT_CONTROLLER_PRIVATE_KEY \ --rpc-url $RPC_URL ``` @@ -98,7 +98,7 @@ cast send $FWSS_ADDRESS \ cast call $FWSS_ADDRESS \ "getDataSetInfo(uint256)" \ 1 \ - --from $FILBEAM_OPERATOR_ADDRESS \ + --from $FILBEAM_OPERATOR_PROXY_ADDRESS \ --rpc-url $RPC_URL ``` @@ -139,14 +139,14 @@ forge create src/FilBeamOperator.sol:FilBeamOperator \ --rpc-url $RPC_URL \ --private-key $OWNER_PRIVATE_KEY -export NEW_IMPLEMENTATION=0x... # New implementation address +export FILBEAM_OPERATOR_IMPL_ADDRESS=0x... # New implementation address ``` #### Step 2: Upgrade the Proxy ```bash # Owner calls upgradeToAndCall on the proxy -cast send $FILBEAM_OPERATOR_ADDRESS \ +cast send $FILBEAM_OPERATOR_PROXY_ADDRESS \ "upgradeToAndCall(address,bytes)" \ $NEW_IMPLEMENTATION \ "0x" \ @@ -158,11 +158,11 @@ cast send $FILBEAM_OPERATOR_ADDRESS \ ```bash # Check new version -cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "version()" --rpc-url $RPC_URL # Should return new version (e.g., "1.1.0") # Verify state is preserved -cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL +cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL ``` ### Rate Change Migration @@ -244,7 +244,7 @@ If critical issues are discovered after migration: ```bash # If FWSS controller was transferred -cast send $FILBEAM_OPERATOR_ADDRESS \ +cast send $FILBEAM_OPERATOR_PROXY_ADDRESS \ "transferFwssFilBeamController(address)" \ $PREVIOUS_CONTROLLER_ADDRESS \ --private-key $OWNER_PRIVATE_KEY \ diff --git a/script/DeployFilBeamOperator.s.sol b/script/DeployFilBeamOperator.s.sol index 7732a9c..288b3d5 100644 --- a/script/DeployFilBeamOperator.s.sol +++ b/script/DeployFilBeamOperator.s.sol @@ -64,20 +64,12 @@ contract DeployFilBeamOperator is Script { vm.startBroadcast(deployerPrivateKey); // Step 1: Deploy the implementation - FilBeamOperator implementation = new FilBeamOperator(); + FilBeamOperator implementation = new FilBeamOperator( + fwssAddress, fwssStateViewAddress, paymentsAddress, cdnRatePerByte, cacheMissRatePerByte + ); // Step 2: Encode the initialize call - bytes memory initializeData = abi.encodeCall( - FilBeamOperator.initialize, - ( - fwssAddress, - fwssStateViewAddress, - paymentsAddress, - cdnRatePerByte, - cacheMissRatePerByte, - filBeamOperatorController - ) - ); + bytes memory initializeData = abi.encodeCall(FilBeamOperator.initialize, (filBeamOperatorController)); //Step 3: Deploy ERC1967 proxy pointing to the implementation ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializeData); diff --git a/script/UpgradeFilBeamOperator.s.sol b/script/UpgradeFilBeamOperator.s.sol new file mode 100644 index 0000000..343d6fe --- /dev/null +++ b/script/UpgradeFilBeamOperator.s.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../src/FilBeamOperator.sol"; + +/** + * @title UpgradeFilBeamOperator + * @dev Upgrades an existing FilBeamOperator proxy to a new implementation + * + * This script deploys a new implementation contract and upgrades the proxy + * State is preserved in the proxy - only the implementation logic changes. + * + * Note: To preserve existing rates, query them from the current proxy: + * cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL + * cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cacheMissRatePerByte()" --rpc-url $RPC_URL + * + * Required Environment Variables: + * - PRIVATE_KEY: Owner's private key (must be contract owner) + * - FILBEAM_OPERATOR_PROXY_ADDRESS: Address of the existing proxy contract + * - FWSS_ADDRESS: Address of the FWSS contract + * - FWSS_STATE_VIEW_ADDRESS: Address of the FWSS State View contract + * - PAYMENTS_ADDRESS: Address of the Payments contract + * - CDN_RATE_PER_BYTE: CDN rate per byte in USDFC smallest units + * - CACHE_MISS_RATE_PER_BYTE: Cache miss rate per byte in USDFC smallest units + * + * Example usage: + * PRIVATE_KEY=0x... FILBEAM_OPERATOR_PROXY_ADDRESS=0x... FWSS_ADDRESS=0x... FWSS_STATE_VIEW_ADDRESS=0x... PAYMENTS_ADDRESS=0x... CDN_RATE_PER_BYTE=100 CACHE_MISS_RATE_PER_BYTE=200 forge script script/UpgradeFilBeamOperator.s.sol --broadcast + */ +contract UpgradeFilBeamOperator is Script { + function run() public { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + // The address of existing proxy contract + address filBeamOperatorProxyAddress = vm.envAddress("FILBEAM_OPERATOR_PROXY_ADDRESS"); + address fwssAddress = vm.envAddress("FWSS_ADDRESS"); + address fwssStateViewAddress = vm.envAddress("FWSS_STATE_VIEW_ADDRESS"); + address paymentsAddress = vm.envAddress("PAYMENTS_ADDRESS"); + uint256 cdnRatePerByte = vm.envUint("CDN_RATE_PER_BYTE"); + uint256 cacheMissRatePerByte = vm.envUint("CACHE_MISS_RATE_PER_BYTE"); + + vm.startBroadcast(deployerPrivateKey); + + // Step 1: Deploy new implementation contract + FilBeamOperator newImplementation = new FilBeamOperator( + fwssAddress, fwssStateViewAddress, paymentsAddress, cdnRatePerByte, cacheMissRatePerByte + ); + + // Step 2: Upgrade the proxy to point to the new implementation + FilBeamOperator proxy = FilBeamOperator(filBeamOperatorProxyAddress); + proxy.upgradeToAndCall(address(newImplementation), ""); + + vm.stopBroadcast(); + + // Step 2: Verify the upgrade + FilBeamOperator filBeam = FilBeamOperator(filBeamOperatorProxyAddress); + console2.log("=== FilBeamOperator Upgrade Complete ==="); + console2.log("Proxy Address:", filBeamOperatorProxyAddress); + console2.log("New Implementation Address:", address(newImplementation)); + console2.log("Contract Version:", filBeam.version()); + } +} diff --git a/src/FilBeamOperator.sol b/src/FilBeamOperator.sol index 6c3a98c..e229662 100644 --- a/src/FilBeamOperator.sol +++ b/src/FilBeamOperator.sol @@ -17,11 +17,11 @@ contract FilBeamOperator is Initializable, Ownable2StepUpgradeable, UUPSUpgradea uint256 maxReportedEpoch; } - address public fwssContractAddress; - address public fwssStateViewContractAddress; - address public paymentsContractAddress; - uint256 public cdnRatePerByte; - uint256 public cacheMissRatePerByte; + address public immutable fwssContractAddress; + address public immutable fwssStateViewContractAddress; + address public immutable paymentsContractAddress; + uint256 public immutable cdnRatePerByte; + uint256 public immutable cacheMissRatePerByte; address public filBeamOperatorController; mapping(uint256 => DataSetUsage) public dataSetUsage; @@ -45,40 +45,41 @@ contract FilBeamOperator is Initializable, Ownable2StepUpgradeable, UUPSUpgradea event FwssFilBeamControllerChanged(address indexed previousController, address indexed newController); /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - /// @notice Initializes the FilBeamOperator contract /// @param _fwssAddress Address of the FWSS contract /// @param _fwssStateViewAddress Address of the FWSS State View Contract /// @param _paymentsAddress Address of the Payments contract /// @param _cdnRatePerByte CDN rate per byte in smallest token units /// @param _cacheMissRatePerByte Cache miss rate per byte in smallest token units - /// @param _filBeamOperatorController Address authorized to record usage and terminate payment rails - function initialize( + constructor( address _fwssAddress, address _fwssStateViewAddress, address _paymentsAddress, uint256 _cdnRatePerByte, - uint256 _cacheMissRatePerByte, - address _filBeamOperatorController - ) public initializer { + uint256 _cacheMissRatePerByte + ) { if (_fwssAddress == address(0)) revert InvalidAddress(); if (_fwssStateViewAddress == address(0)) revert InvalidAddress(); if (_paymentsAddress == address(0)) revert InvalidAddress(); if (_cdnRatePerByte == 0 || _cacheMissRatePerByte == 0) revert InvalidRate(); - if (_filBeamOperatorController == address(0)) revert InvalidAddress(); - - __Ownable_init(msg.sender); - __Ownable2Step_init(); - __UUPSUpgradeable_init(); fwssContractAddress = _fwssAddress; fwssStateViewContractAddress = _fwssStateViewAddress; paymentsContractAddress = _paymentsAddress; cdnRatePerByte = _cdnRatePerByte; cacheMissRatePerByte = _cacheMissRatePerByte; + + _disableInitializers(); + } + + /// @notice Initializes the FilBeamOperator contract + /// @param _filBeamOperatorController Address authorized to record usage and terminate payment rails + function initialize(address _filBeamOperatorController) public initializer { + if (_filBeamOperatorController == address(0)) revert InvalidAddress(); + + __Ownable_init(msg.sender); + __Ownable2Step_init(); + __UUPSUpgradeable_init(); + filBeamOperatorController = _filBeamOperatorController; } diff --git a/test/FilBeamOperator.t.sol b/test/FilBeamOperator.t.sol index 81d052e..3115be0 100644 --- a/test/FilBeamOperator.t.sol +++ b/test/FilBeamOperator.t.sol @@ -59,20 +59,16 @@ contract FilBeamOperatorTest is Test { mockPayments = new MockPayments(); // Deploy implementation - implementation = new FilBeamOperator(); + implementation = new FilBeamOperator( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE + ); // Encode intialize call - bytes memory initializeData = abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ); + bytes memory initializeData = abi.encodeCall(FilBeamOperator.initialize, (filBeamOperatorController)); // Deploy proxy proxy = new ERC1967Proxy(address(implementation), initializeData); @@ -206,111 +202,43 @@ contract FilBeamOperatorTest is Test { } function test_InitializeRevertZeroAddress() public { - FilBeamOperator impl = new FilBeamOperator(); - vm.expectRevert(InvalidAddress.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(0), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + new FilBeamOperator( + address(0), address(mockStateView), address(mockPayments), CDN_RATE_PER_BYTE, CACHE_MISS_RATE_PER_BYTE ); vm.expectRevert(InvalidAddress.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(0), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + new FilBeamOperator( + address(mockFWSS), address(0), address(mockPayments), CDN_RATE_PER_BYTE, CACHE_MISS_RATE_PER_BYTE ); vm.expectRevert(InvalidAddress.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(0), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + new FilBeamOperator( + address(mockFWSS), address(mockStateView), address(0), CDN_RATE_PER_BYTE, CACHE_MISS_RATE_PER_BYTE ); } function test_InitializeRevertZeroRate() public { - FilBeamOperator impl = new FilBeamOperator(); - vm.expectRevert(InvalidRate.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - 0, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + new FilBeamOperator( + address(mockFWSS), address(mockStateView), address(mockPayments), 0, CACHE_MISS_RATE_PER_BYTE ); vm.expectRevert(InvalidRate.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - 0, - filBeamOperatorController - ) - ) - ); + new FilBeamOperator(address(mockFWSS), address(mockStateView), address(mockPayments), CDN_RATE_PER_BYTE, 0); } function test_InitializeRevertZeroFilBeamController() public { - FilBeamOperator impl = new FilBeamOperator(); + FilBeamOperator impl = new FilBeamOperator( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE + ); vm.expectRevert(InvalidAddress.selector); - new ERC1967Proxy( - address(impl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - address(0) - ) - ) - ); + new ERC1967Proxy(address(impl), abi.encodeCall(FilBeamOperator.initialize, (address(0)))); } function test_ReportUsageRollup() public { @@ -1464,21 +1392,16 @@ contract FilBeamOperatorTest is Test { function test_TransferFwssFilBeamController_NewOperatorCanCallAfterMigration() public { // Deploy a new FilBeamOperator instance to act as the new operator - FilBeamOperator newOperatorImpl = new FilBeamOperator(); + FilBeamOperator newOperatorImpl = new FilBeamOperator( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE + ); ERC1967Proxy newOperatorProxy = new ERC1967Proxy( - address(newOperatorImpl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + address(newOperatorImpl), abi.encodeCall(FilBeamOperator.initialize, (filBeamOperatorController)) ); FilBeamOperator newOperator = FilBeamOperator(address(newOperatorProxy)); @@ -1508,21 +1431,16 @@ contract FilBeamOperatorTest is Test { function test_TransferFwssFilBeamController_IntegrationFlow() public { // Deploy new operator - FilBeamOperator newOperatorImpl = new FilBeamOperator(); + FilBeamOperator newOperatorImpl = new FilBeamOperator( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE + ); ERC1967Proxy newOperatorProxy = new ERC1967Proxy( - address(newOperatorImpl), - abi.encodeCall( - FilBeamOperator.initialize, - ( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ) - ) + address(newOperatorImpl), abi.encodeCall(FilBeamOperator.initialize, (filBeamOperatorController)) ); FilBeamOperator newOperator = FilBeamOperator(address(newOperatorProxy)); @@ -1568,30 +1486,22 @@ contract FilBeamOperatorTest is Test { function test_CannotInitializeTwice() public { vm.expectRevert(); - filBeam.initialize( - address(mockFWSS), - address(mockStateView), - address(mockPayments), - CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController - ); + filBeam.initialize(filBeamOperatorController); } function test_CannotInitializeImplementationDirectly() public { vm.expectRevert(); - implementation.initialize( + implementation.initialize(filBeamOperatorController); + } + + function test_OnlyOwnerCanUpgrade() public { + FilBeamOperator newImpl = new FilBeamOperator( address(mockFWSS), address(mockStateView), address(mockPayments), CDN_RATE_PER_BYTE, - CACHE_MISS_RATE_PER_BYTE, - filBeamOperatorController + CACHE_MISS_RATE_PER_BYTE ); - } - - function test_OnlyOwnerCanUpgrade() public { - FilBeamOperator newImpl = new FilBeamOperator(); vm.prank(user1); vm.expectRevert(); @@ -1610,7 +1520,13 @@ contract FilBeamOperatorTest is Test { address ownerBefore = filBeam.owner(); // Deploy new implementation - FilBeamOperator newImpl = new FilBeamOperator(); + FilBeamOperator newImpl = new FilBeamOperator( + address(mockFWSS), + address(mockStateView), + address(mockPayments), + CDN_RATE_PER_BYTE, + CACHE_MISS_RATE_PER_BYTE + ); filBeam.upgradeToAndCall(address(newImpl), ""); // Get state after upgrade From 924665d6fe004bfb6bf3eb433b11b260659fc686 Mon Sep 17 00:00:00 2001 From: Chaitu-Tatipamula Date: Mon, 26 Jan 2026 22:57:09 +0530 Subject: [PATCH 3/4] docs: address PR review feedback on documentation and contract versioning --- DEPLOYMENT.md | 23 ++++++++++++++++++++--- README.md | 5 ----- script/UpgradeFilBeamOperator.s.sol | 2 +- src/FilBeamOperator.sol | 4 +++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 0747dc6..d062988 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -129,17 +129,34 @@ FilBeamOperator supports two types of upgrades: ### UUPS Upgrade (Bug Fixes / Features) -Use this for logic changes that don't require modifying rates. +Use this for logic changes that don't require modifying rates. The preferred method is using the provided upgrade script as it is safer and handles everything atomically. -#### Step 1: Deploy New Implementation +#### Method 1: Scripted Upgrade (Preferred) +The [UpgradeFilBeamOperator.s.sol](./script/UpgradeFilBeamOperator.s.sol) script handles both deploying the new implementation and performing the upgrade in a single atomic broadcast + +```bash +# Required env vars: +# PRIVATE_KEY, FILBEAM_OPERATOR_PROXY_ADDRESS, FWSS_ADDRESS, +# FWSS_STATE_VIEW_ADDRESS, PAYMENTS_ADDRESS, CDN_RATE_PER_BYTE, CACHE_MISS_RATE_PER_BYTE +# Note: Rates are immutable. To keep current rates, query the proxy first: +# cast call $PROXY "cdnRatePerByte()" --rpc-url $RPC_URL +forge script script/UpgradeFilBeamOperator.s.sol \ +``` + +#### Method 2: Manual Upgrade (Advanced) + +If you prefer manual control or need to separate the deployment and upgrade steps: + +**Step 1: Deploy New Implementation** ```bash # Deploy only the new implementation contract forge create src/FilBeamOperator.sol:FilBeamOperator \ + --constructor-args $FWSS_ADDRESS $FWSS_STATE_VIEW_ADDRESS $PAYMENTS_ADDRESS $CDN_RATE_PER_BYTE $CACHE_MISS_RATE_PER_BYTE \ --rpc-url $RPC_URL \ --private-key $OWNER_PRIVATE_KEY -export FILBEAM_OPERATOR_IMPL_ADDRESS=0x... # New implementation address +export NEW_IMPLEMENTATION=0x... # From output above ``` #### Step 2: Upgrade the Proxy diff --git a/README.md b/README.md index 597ae29..a63f36e 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,6 @@ The contract is initialized with the following parameters: ```solidity function initialize( - address fwssAddress, // FWSS contract address - address fwssStateViewAddress, // FWSS State View contract address - address paymentsAddress, // Payments contract address for rail management - uint256 cdnRatePerByte, // Rate per byte for CDN usage - uint256 cacheMissRatePerByte, // Rate per byte for cache-miss usage address filBeamOperatorController // Address authorized to report usage ) ``` diff --git a/script/UpgradeFilBeamOperator.s.sol b/script/UpgradeFilBeamOperator.s.sol index 343d6fe..02fe5ae 100644 --- a/script/UpgradeFilBeamOperator.s.sol +++ b/script/UpgradeFilBeamOperator.s.sol @@ -52,7 +52,7 @@ contract UpgradeFilBeamOperator is Script { vm.stopBroadcast(); - // Step 2: Verify the upgrade + // Step 3: Verify the upgrade FilBeamOperator filBeam = FilBeamOperator(filBeamOperatorProxyAddress); console2.log("=== FilBeamOperator Upgrade Complete ==="); console2.log("Proxy Address:", filBeamOperatorProxyAddress); diff --git a/src/FilBeamOperator.sol b/src/FilBeamOperator.sol index e229662..a47d728 100644 --- a/src/FilBeamOperator.sol +++ b/src/FilBeamOperator.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.13; import "./Errors.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {FilecoinPayV1} from "@filecoin-pay/FilecoinPayV1.sol"; @@ -248,6 +247,9 @@ contract FilBeamOperator is Initializable, Ownable2StepUpgradeable, UUPSUpgradea /// @param newImplementation Address of new implementation contract function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + /// @notice Returns the current version of the implementation + /// @dev This version must be incremented manually with each new implementation + /// deployment to allow tracking upgrades on chain. function version() public pure virtual returns (string memory) { return "1.0.0"; } From 6ca1cc98e74c84a9c43091e56f0092d11d3f1bad Mon Sep 17 00:00:00 2001 From: Chaitu-Tatipamula <107246959+Chaitu-Tatipamula@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:43:38 +0530 Subject: [PATCH 4/4] docs: add missing flags to upgrade script command --- DEPLOYMENT.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index d062988..ae399e3 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -142,6 +142,8 @@ The [UpgradeFilBeamOperator.s.sol](./script/UpgradeFilBeamOperator.s.sol) script # Note: Rates are immutable. To keep current rates, query the proxy first: # cast call $PROXY "cdnRatePerByte()" --rpc-url $RPC_URL forge script script/UpgradeFilBeamOperator.s.sol \ + --rpc-url $RPC_URL \ + --private-key $OWNER_PRIVATE_KEY ``` #### Method 2: Manual Upgrade (Advanced)