Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 89 additions & 16 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -50,22 +56,27 @@ forge script script/DeployFilBeamOperator.s.sol \

# Expected output:
# === FilBeamOperator Deployment Complete ===
# FilBeamOperator deployed at: 0x...
# === Configuration ===
# FWSS address: <fwss_address>
# Payments 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_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_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
Expand All @@ -74,7 +85,7 @@ cast call $FILBEAM_OPERATOR_ADDRESS "filBeamOperatorController()" --rpc-url $RPC
# 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
```
Expand All @@ -87,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
```

Expand All @@ -111,9 +122,71 @@ 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. The preferred method is using the provided upgrade script as it is safer and handles everything atomically.

#### 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 \
Comment thread
Chaitu-Tatipamula marked this conversation as resolved.
--rpc-url $RPC_URL \
--private-key $OWNER_PRIVATE_KEY
```

#### 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 NEW_IMPLEMENTATION=0x... # From output above
Comment thread
pyropy marked this conversation as resolved.
```

#### Step 2: Upgrade the Proxy

```bash
# Owner calls upgradeToAndCall on the proxy
cast send $FILBEAM_OPERATOR_PROXY_ADDRESS \
Comment thread
Chaitu-Tatipamula marked this conversation as resolved.
"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_PROXY_ADDRESS "version()" --rpc-url $RPC_URL
# Should return new version (e.g., "1.1.0")

# Verify state is preserved
cast call $FILBEAM_OPERATOR_PROXY_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

Expand Down Expand Up @@ -190,7 +263,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 \
Expand Down
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -39,15 +40,15 @@ $ 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(
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
function initialize(
address filBeamOperatorController // Address authorized to report usage
)
```

Expand All @@ -59,6 +60,7 @@ Deploy the contract using Forge script:
PRIVATE_KEY=<deployer_private_key> \
FILBEAM_CONTROLLER=<filbeam_controller_address> \
FWSS_ADDRESS=<fwss_contract_address> \
FWSS_STATE_VIEW_ADDRESS=<fwss_state_view_address> \
CDN_PRICE_USD_PER_TIB=<cdn_price_usd_per_tib> \
CACHE_MISS_PRICE_USD_PER_TIB=<cache_miss_price_usd_per_tib> \
PRICE_DECIMALS=<price_decimals> \
Expand All @@ -67,6 +69,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
Expand Down Expand Up @@ -104,6 +110,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
Expand All @@ -122,6 +134,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
Expand Down
27 changes: 19 additions & 8 deletions script/DeployFilBeamOperator.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -62,20 +63,30 @@ 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(
fwssAddress, fwssStateViewAddress, paymentsAddress, cdnRatePerByte, cacheMissRatePerByte
);

// Step 2: Encode the initialize call
bytes memory initializeData = abi.encodeCall(FilBeamOperator.initialize, (filBeamOperatorController));

//Step 3: Deploy ERC1967 proxy pointing to the implementation
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializeData);
Comment thread
Chaitu-Tatipamula marked this conversation as resolved.

// 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 ===");
Expand Down
62 changes: 62 additions & 0 deletions script/UpgradeFilBeamOperator.s.sol
Original file line number Diff line number Diff line change
@@ -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 3: 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());
}
}
Loading