Skip to content

feat: Make FilBeamOperator contract upgradeable#13

Open
Chaitu-Tatipamula wants to merge 4 commits intofilbeam:mainfrom
Chaitu-Tatipamula:feat/uups-upgradeable
Open

feat: Make FilBeamOperator contract upgradeable#13
Chaitu-Tatipamula wants to merge 4 commits intofilbeam:mainfrom
Chaitu-Tatipamula:feat/uups-upgradeable

Conversation

@Chaitu-Tatipamula
Copy link

Description

This PR implements the UUPS (Universal Upgradeable Proxy Standard) proxy pattern for the FilBeamOperator contract, enabling safe contract upgrades while preserving all state.

Closes #3

Motivation

  • Enable bug fixes and feature additions without redeploying a new contract
  • Preserve state (usage data, ownership, configuration) across upgrades
  • Maintain a stable contract address for integrations

Key Implementation Details

  • Inherits Initializable, Ownable2StepUpgradeable, UUPSUpgradeable
  • initialize() replaces constructor (can only be called once)
  • _disableInitializers() prevents implementation contract initialization
  • _authorizeUpgrade() restricted to owner
  • version() function returns "1.0.0"

Testing

  • 74 tests pass including new upgrade tests:
    • test_CannotInitializeTwice
    • test_CannotInitializeImplementationDirectly
    • test_OnlyOwnerCanUpgrade
    • test_OwnerCanUpgrade
    • test_Version

How to Test

forge build
forge test -vvv

Copy link
Contributor

@pyropy pyropy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start! 👏🏻

I believe we're missing two very important things:

  • Deployment script that upgrade the old contract without deploying a new proxy
  • State view contract (I'm not sure if this is needed with UUPS contract but I've seen that you have mentioned it in the docs).

It is very important to us NOT to lose on-chain state during upgrades as that could potentially cause loss for both FilBeam and storage providers.

);

//Step 3: Deploy ERC1967 proxy pointing to the implementation
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializeData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this will always deploy a new proxy contract? Should there be a separate script or way to upgrade proxy to a new implementation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes i think having a seperate script for this is nice, so i'll set it to take proxy address as an environment variable, deploy new impl and call upgradeToAndCall on the proxy

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 23 to 24
uint256 public cdnRatePerByte;
uint256 public cacheMissRatePerByte;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint256 public cdnRatePerByte;
uint256 public cacheMissRatePerByte;
uint256 public immutable cdnRatePerByte;
uint256 public immutable cacheMissRatePerByte;

One of the decisions we have made during this contract development is to leave the rates immutable. Why is that you may wonder? It's because the way of how we actually report the usage.

So you see, we may change the rate from current $7 to $8, but there is no way for us to know if the reported egress usage happened before or after the price change. Therefore it would be unfair to charge users a new rate for egress usage that happened under the old rate.

If the rate change happens we'd have to deploy a new set of contracts, but upgradability is there to ensure that we can issue bugfixes without copying the state between smart contracts.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah now i get it, i was wondering why they were immutable. Thanks for letting me know-this makes total sense, this ensures fair billing without any ambiguity on which rate applies to the reported usage

Comment on lines 20 to 22
address public fwssContractAddress;
address public fwssStateViewContractAddress;
address public paymentsContractAddress;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
address public fwssContractAddress;
address public fwssStateViewContractAddress;
address public paymentsContractAddress;
address public immutable fwssContractAddress;
address public immutable fwssStateViewContractAddress;
address public immutable paymentsContractAddress;

Since these are set during the initialization, I believe they may remain immutable. FWSS contract is doing a similar thing

https://github.com/FilOzone/filecoin-services/blob/a86e4a5018133f17a25b4bb6b5b99da4d34fe664/service_contracts/src/FilecoinWarmStorageService.sol#L232-L238

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it made them immutable and updated the scripts

DEPLOYMENT.md Outdated
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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL # Should return "1.0.0"
cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "version()" --rpc-url $RPC_URL # Should return "1.0.0"

Let's perhaps make the exported variable more explicit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it

DEPLOYMENT.md Outdated

```bash
# Owner calls upgradeToAndCall on the proxy
cast send $FILBEAM_OPERATOR_ADDRESS \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cast send $FILBEAM_OPERATOR_ADDRESS \
cast send $FILBEAM_OPERATOR_PROXY_ADDRESS \

DEPLOYMENT.md Outdated
--rpc-url $RPC_URL \
--private-key $OWNER_PRIVATE_KEY

export NEW_IMPLEMENTATION=0x... # New implementation address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export NEW_IMPLEMENTATION=0x... # New implementation address
export FILBEAM_OPERATOR_IMPL_ADDRESS=0x... # New implementation address

DEPLOYMENT.md Outdated

```bash
# Check new version
cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cast call $FILBEAM_OPERATOR_ADDRESS "version()" --rpc-url $RPC_URL
cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "version()" --rpc-url $RPC_URL

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

DEPLOYMENT.md Outdated
# Should return new version (e.g., "1.1.0")

# Verify state is preserved
cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cast call $FILBEAM_OPERATOR_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL
cast call $FILBEAM_OPERATOR_PROXY_ADDRESS "cdnRatePerByte()" --rpc-url $RPC_URL

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done i changed it

README.md Outdated
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing a state view contract 👀

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state view contract is already integrated in FilBeamOperator.sol as fwssStateViewContractAddress it is used in _settlePaymentRail to actually query rail IDs, i just documented this existing parameter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, great catch! For a moment I thought it was a FilBeamStateViewContract.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes as you said i dont think we may need a seperate FilBeamOperatorStateView contract since all the states are accessible via the public getters

Copy link
Contributor

@pyropy pyropy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Let's upgrade the documentation before proceeding to merge this.

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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

Please remove this import as it's replaced by Ownable2StepUpgradeable

README.md Outdated
Comment on lines 55 to 56
uint256 cacheMissRatePerByte, // Rate per byte for cache-miss usage
address filBeamOperatorController // Address authorized to report usage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint256 cacheMissRatePerByte, // Rate per byte for cache-miss usage
address filBeamOperatorController // Address authorized to report usage
uint256 cacheMissRatePerByte // Rate per byte for cache-miss usage

filBeamOperatorController is removed from the initialize method.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean filBeamOperatorController is the only thing kept in initialize method! the addresses and the rates were the ones moved to constructor to be immutable

/// @param newImplementation Address of new implementation contract
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function version() public pure virtual returns (string memory) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version() function returns a hardcoded "1.0.0" - consider documenting that this must be updated manually with each upgrade.


```bash
# Owner calls upgradeToAndCall on the proxy
cast send $FILBEAM_OPERATOR_PROXY_ADDRESS \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manual upgrade instructions using forge create are good for documentation, but consider referencing the new UpgradeFilBeamOperator.s.sol script as
the preferred method


vm.stopBroadcast();

// Step 2: Verify the upgrade
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Step 2: Verify the upgrade
// Step 3: Verify the upgrade

@pyropy pyropy requested review from bajtos and juliangruber January 26, 2026 11:47
@juliangruber juliangruber removed their request for review January 28, 2026 08:07
@pyropy
Copy link
Contributor

pyropy commented Feb 4, 2026

@juliangruber would you also be able to give this a look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make FilBeamOperator contract upgradable

2 participants