Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5c63bc9
feat: add OpenZeppelin upgradeable contracts dependencies
pyropy Sep 29, 2025
7b15d0e
feat: add FilBeam contract infrastructure
pyropy Sep 29, 2025
d9ea814
feat: implement upgradeable FilBeam contract
pyropy Sep 29, 2025
0b5b4e3
test: add comprehensive FilBeam test suite
pyropy Sep 29, 2025
7b83b93
feat: add deployment script with USDFC integration
pyropy Sep 29, 2025
4f61629
chore: remove unused Counter contract files
pyropy Sep 29, 2025
d6e3eb6
docs: add project specification and update documentation
pyropy Sep 29, 2025
c1e64ed
feat: add batch usage reporting with reportUsageRollupBatch method
pyropy Sep 29, 2025
733c495
feat: add decimal pricing support for deployment script
pyropy Sep 29, 2025
9ea164a
feat: add batch settlement methods for payment rails
pyropy Sep 29, 2025
dd81176
docs: add comprehensive deployment guide and API documentation
pyropy Sep 29, 2025
7f97814
refactor: simplify epoch validation using maxReportedEpoch only
pyropy Sep 29, 2025
2bfb0ab
feat: add filBeamController role for access control
pyropy Sep 30, 2025
b460676
refactor: remove upgradeable pattern and use direct deployment
pyropy Sep 30, 2025
b27c24a
feat: add setFilBeamController method for updating authorized controller
pyropy Sep 30, 2025
567bc87
refactor: make deployer the default owner, remove initialOwner parameter
pyropy Sep 30, 2025
94baf6f
docs: update README with FILBEAM_CONTROLLER and remove proxy references
pyropy Sep 30, 2025
0692c8a
refactor: use InvalidAddress error for zero address validations
pyropy Sep 30, 2025
2ab1bba
Remove OpenZeppelin/openzeppelin-contracts-upgradeable
pyropy Sep 30, 2025
50b3bd9
refactor: improve FilBeam contract with gas optimizations and enhance…
pyropy Sep 30, 2025
b6c7244
Update AGENTS.md
pyropy Sep 30, 2025
e8ba6ab
docs: update README and SPEC to match current contract API
pyropy Oct 1, 2025
c2028ce
chore: Do not validate data set in terminateCDNPaymentRails
pyropy Oct 1, 2025
f2e6e51
chore: Cleanup docs and fix deployment example
pyropy Oct 1, 2025
98f47cd
fix: move last settlement epoch assignment after settlement
pyropy Oct 1, 2025
69814c0
refactor: rename FilBeam to FilBeamOperator and update parameter names
pyropy Oct 7, 2025
7ec4fb6
refactor: rename settleCDNPaymentRails to settleFilBeamPaymentRails
pyropy Oct 7, 2025
1f34058
refactor: remove single operation methods, use batch-only API
pyropy Oct 7, 2025
71db784
refactor: rename batch methods to use plural form
pyropy Oct 7, 2025
5d28a4d
refactor: renamed variable filBeamController to filBeamOperatorContro…
pyropy Oct 7, 2025
e87edf9
feat: read USDFC address from FWSS contract in deployment script
pyropy Oct 7, 2025
ac6abe5
chore: Update README.md deployment example
pyropy Oct 7, 2025
2c6c242
refactor: rename setFilBeamController to setFilBeamOperatorController
pyropy Oct 7, 2025
eed86bc
feat: calculate settlement amounts at report time instead of settlem…
pyropy Oct 7, 2025
7f5870e
chore: update readme
pyropy Oct 7, 2025
8cd48ab
chore: add methods docs
pyropy Oct 8, 2025
ca5b0cb
refactor: make CDN and cache miss rates immutable
pyropy Oct 10, 2025
cdcc16c
refactor: make settlement methods non-reverting for better UX
pyropy Oct 10, 2025
745e157
feat: refactor payment rail settlements for partial payments and simp…
pyropy Oct 10, 2025
7bf5726
refactor: save fwss and payments address
pyropy Oct 10, 2025
e35cb62
chore: update deployment example
pyropy Oct 13, 2025
7ffb6c9
chore: add toEpoch to UsageReported and rename epoch to toEpoch
pyropy Oct 14, 2025
28dbd65
chore: update UsageReported event signature in SPEC.md
pyropy Oct 14, 2025
86cd989
refactor: report usage up to single epoch for all data sets
pyropy Oct 15, 2025
1d74f6b
chore: forge fmt
pyropy Oct 15, 2025
014498d
Update script/DeployFilBeamOperator.s.sol
pyropy Oct 17, 2025
1e5f146
refactor: unify payment rail settlement into one internal method
pyropy Oct 23, 2025
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
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/filecoin-pay"]
path = lib/filecoin-pay
url = https://github.com/filozone/filecoin-pay
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Bash commands
- `forge fmt`: Format the project
- `forge test`: Test the project
- `forge install`: Install dependencies

# Workflow
- Make sure to run `forge fmt` when you're done making a series of test changes
- Prefer running single tests, and not the whole test suite, for performance
- Update [SPEC.md](SPEC.md) and [README.md](README.md) when making changes to the contract API
109 changes: 90 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
## Foundry
# FilBeamOperator Contract

FilBeamOperator is a smart contract used for aggregating CDN and cache-miss usage data and managing payment settlements for CDN payment rails operated by [Filecoin Warm Storage Service](https://github.com/FilOzone/filecoin-services).

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
## Features

Foundry consists of:
- **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

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
## Foundry

## Documentation
Documentation: https://book.getfoundry.sh/

https://book.getfoundry.sh/
## Prerequisites
- [Foundry](https://getfoundry.sh/) - Ethereum development toolchain

## Usage
### Usage

### Build

Expand All @@ -33,24 +35,93 @@ $ forge test
$ forge fmt
```

### Gas Snapshots
### Deploy FilBeamOperator Contract

```shell
$ forge snapshot
The FilBeamOperator contract requires the following constructor 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
)
```

### Anvil
#### Deployment Example

Deploy the contract using Forge script:

```bash
PRIVATE_KEY=<deployer_private_key> \
FILBEAM_CONTROLLER=<filbeam_controller_address> \
PAYMENTS_ADDRESS=<filecoin_pay_contract_address> \
FWSS_ADDRESS=<fwss_contract_address> \
PAYMENTS_ADDRESS=<payments_contract_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> \
forge script script/DeployFilBeamOperator.s.sol \
--rpc-url <your_rpc_url> \
--broadcast
```

```shell
$ anvil
**Note**: The deployer address automatically becomes the contract owner.

## Contract API

### Usage Reporting

```solidity
function recordUsageRollups(
uint256 toEpoch,
uint256[] calldata dataSetIds,
uint256[] calldata cdnBytesUsed,
uint256[] calldata cacheMissBytesUsed
) external onlyFilBeamOperatorController
```

### Deploy
### Settlement Operations

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```solidity
function settleCDNPaymentRails(uint256[] calldata dataSetIds) external
function settleCacheMissPaymentRails(uint256[] calldata dataSetIds) external
```

### Data Set Management

**Payment Rail Termination**
```solidity
function terminateCDNPaymentRails(uint256 dataSetId) external onlyFilBeamOperatorController
```

### Contract Management

**Ownership & Controller**
```solidity
function transferOwnership(address newOwner) external onlyOwner
function setFilBeamOperatorController(address _filBeamOperatorController) external onlyOwner
```

## Key Concepts

### Batch Operations
- **Gas Efficient**: Reduce transaction costs for bulk operations
- **Atomic**: All operations in a batch succeed or all fail
- **Independent Rails**: CDN and cache-miss settlements operate independently

### Pricing Model
- **Usage-Based**: Calculated as `usage_bytes * rate_per_byte` at report time
- **Immutable Rates**: Rates are set at deployment and cannot be changed, ensuring predictable pricing
- **Transparent Pricing**: All users can view the fixed rates on-chain
- **Partial Settlements**: Supports partial settlements when accumulated amount exceeds payment rail's `lockupFixed`

### Rail Settlement
- **Independent Tracking**: CDN and cache-miss settlements tracked separately
- **Epoch-Based**: Settlement periods defined by epoch ranges
- **Accumulative**: Usage accumulates between settlements

### Cast

```shell
Expand Down
203 changes: 203 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
## Specification

### FilBeamOperator (Operator) Contract

#### Overview

The Filecoin Beam (FilBeamOperator) contract is responsible for managing CDN (cache-hit) and cache-miss data set egress usage data reported by the off-chain rollup worker and settlement of payment rails. Payment rails are managed by the Filecoin Warm Storage Service (FWSS) contract. The FilBeamOperator contract interacts with the FWSS contract to facilitate fund transfers based on reported usage data with rate-based billing.

#### Initialization
**Method**: `constructor(address fwssAddress, uint256 _cdnRatePerByte, uint256 _cacheMissRatePerByte, address _filBeamOperatorController)`

**Parameters**:
- `address fwssAddress`: Address of the FWSS contract
- `uint256 _cdnRatePerByte`: Rate per byte for CDN usage billing (must be > 0)
- `uint256 _cacheMissRatePerByte`: Rate per byte for cache-miss usage billing (must be > 0)
- `address _filBeamOperatorController`: Address authorized to report usage and terminate payment rails

**Owner**:
- The deployer (msg.sender) automatically becomes the contract owner

**Validations**:
- FWSS address cannot be zero address
- Both rates must be greater than zero
- FilBeamOperator controller cannot be zero address

#### Data Structure
**DataSetUsage Struct**:
- `uint256 cdnAmount`: Accumulated CDN settlement amount between settlements (calculated at report time)
- `uint256 cacheMissAmount`: Accumulated cache-miss settlement amount between settlements (calculated at report time)
- `uint256 maxReportedEpoch`: Highest epoch number reported for this dataset (0 indicates uninitialized dataset)
- `uint256 lastCDNSettlementEpoch`: Last epoch settled for CDN payment rail
- `uint256 lastCacheMissSettlementEpoch`: Last epoch settled for cache-miss payment rail

#### Usage Reporting

**Method**: `recordUsageRollups(uint256 toEpoch, uint256[] dataSetIds, uint256[] cdnBytesUsed, uint256[] cacheMissBytesUsed)`

- **Access**: FilBeamOperator controller only
- **Purpose**: Accepts multiple usage reports in a single transaction for improved gas efficiency
- **toEpoch Parameter**: Single epoch number up to which usage is reported for all datasets in the batch
- **Epoch Requirements**:
- Epoch must be > 0
- Epoch must be greater than previously reported epochs for each dataset
- Each epoch can only be reported once per dataset
- **Usage Requirements**:
- Usage is converted to settlement amounts using current rates at report time
- Amounts accumulate in the dataset between settlements
- **Parameter Requirements**:
- All arrays must have equal length
- All datasets in the batch report usage up to the same toEpoch
- **Batch Processing**:
- Processes all reports atomically (all succeed or all fail)
- Maintains epoch ordering and validation rules per dataset
- Prevents duplicate epoch reporting within the batch
- **State Updates**:
- Initialize dataset on first report (sets maxReportedEpoch to non-zero value)
- Calculate amounts: `cdnAmount = cdnBytesUsed * cdnRatePerByte`, `cacheMissAmount = cacheMissBytesUsed * cacheMissRatePerByte`
- Accumulate calculated amounts
- Update max reported epoch to toEpoch
- **Events**: Emits individual `UsageReported` event for each processed report (contains bytes, not amounts)

#### Payment Rail Settlement

**Method**: `settleCDNPaymentRails(uint256[] dataSetIds)`

- **Access**: Publicly callable (anyone can trigger settlement)
- **Purpose**: Settles CDN payment rails for multiple datasets in a single transaction
- **Calculation Period**: From last CDN settlement epoch + 1 to max reported epoch
- **Settlement Logic**:
- Retrieves rail ID from FWSS DataSetInfo
- Fetches rail details from Payments contract to get `lockupFixed`
- Calculates settleable amount: `min(accumulated_amount, rail.lockupFixed)`
- Only calls FWSS contract if settleable amount > 0
- Reduces accumulated CDN amount by settled amount (may leave remainder)
- **State Updates**:
- Update last CDN settlement epoch to max reported epoch
- Reduce accumulated amount by settled amount (not reset to zero if partial)
- **Requirements**: None - gracefully skips datasets that cannot be settled
- **Batch Processing**:
- Processes each dataset independently (non-reverting)
- Skips uninitialized datasets or those without new usage
- Skips datasets without valid rail configuration
- Continues processing even if some datasets cannot be settled
- **Partial Settlement**: Supports partial settlements when `accumulated_amount > lockupFixed`
- **Events**: Emits `CDNSettlement` event with actual settled amount (may be less than accumulated)
- **Independent Operation**: Can be called independently of cache-miss settlement

**Method**: `settleCacheMissPaymentRails(uint256[] dataSetIds)`

- **Access**: Publicly callable (typically called by Storage Providers)
- **Purpose**: Settles cache-miss payment rails for multiple datasets in a single transaction
- **Calculation Period**: From last cache-miss settlement epoch + 1 to max reported epoch
- **Settlement Logic**:
- Retrieves rail ID from FWSS DataSetInfo
- Fetches rail details from Payments contract to get `lockupFixed`
- Calculates settleable amount: `min(accumulated_amount, rail.lockupFixed)`
- Only calls FWSS contract if settleable amount > 0
- Reduces accumulated cache-miss amount by settled amount (may leave remainder)
- **State Updates**:
- Update last cache-miss settlement epoch to max reported epoch
- Reduce accumulated amount by settled amount (not reset to zero if partial)
- **Requirements**: None - gracefully skips datasets that cannot be settled
- **Batch Processing**:
- Processes each dataset independently (non-reverting)
- Skips uninitialized datasets or those without new usage
- Skips datasets without valid rail configuration
- Continues processing even if some datasets cannot be settled
- **Partial Settlement**: Supports partial settlements when `accumulated_amount > lockupFixed`
- **Events**: Emits `CacheMissSettlement` event with actual settled amount (may be less than accumulated)
- **Independent Operation**: Can be called independently of CDN settlement

#### Payment Rail Termination
**Method**: `terminateCDNPaymentRails(uint256 dataSetId)`

- **Access**: FilBeamOperator controller only
- **Requirements**: Dataset must be initialized
- **Process**: Forward termination call to FWSS contract
- **Events**: Emits `PaymentRailsTerminated` event

#### Data Access
**Method**: `getDataSetUsage(uint256 dataSetId)`

**Returns**:
- `uint256 cdnAmount`: Current accumulated CDN settlement amount
- `uint256 cacheMissAmount`: Current accumulated cache-miss settlement amount
- `uint256 maxReportedEpoch`: Highest reported epoch (0 indicates uninitialized dataset)
- `uint256 lastCDNSettlementEpoch`: Last CDN settlement epoch
- `uint256 lastCacheMissSettlementEpoch`: Last cache-miss settlement epoch

#### Ownership Management
**Method**: `transferOwnership(address newOwner)`

- **Access**: Contract owner only
- **Requirements**: New owner cannot be zero address
- **Purpose**: Transfer contract ownership

#### FilBeamOperator Controller Management
**Method**: `setFilBeamOperatorController(address _filBeamOperatorController)`

- **Access**: Contract owner only
- **Requirements**: FilBeamOperator controller cannot be zero address
- **Purpose**: Update the authorized address for usage reporting and payment rail termination
- **Events**: Emits `FilBeamOperatorControllerUpdated` event

#### Events
- `UsageReported(uint256 indexed dataSetId, uint256 indexed fromEpoch, uint256 indexed toEpoch, uint256 cdnBytesUsed, uint256 cacheMissBytesUsed)`
- `CDNSettlement(uint256 indexed dataSetId, uint256 fromEpoch, uint256 toEpoch, uint256 cdnAmount)`
- `CacheMissSettlement(uint256 indexed dataSetId, uint256 fromEpoch, uint256 toEpoch, uint256 cacheMissAmount)`
- `PaymentRailsTerminated(uint256 indexed dataSetId)`
- `FilBeamOperatorControllerUpdated(address indexed oldController, address indexed newController)`

#### Access Control
- **Owner**: Address authorized to manage contract ownership and set FilBeamOperator controller
- **FilBeamOperator Controller**: Address authorized to report usage and terminate payment rails

#### Error Conditions
- `OwnableUnauthorizedAccount(address)`: Caller is not the contract owner
- `Unauthorized()`: Caller is not the FilBeamOperator controller
- `InvalidEpoch()`: Invalid epoch number or ordering (used in usage reporting)
- `InvalidUsageAmount()`: Invalid array lengths in batch operations
- `InvalidRate()`: Invalid rate configuration (zero rates at deployment)
- `InvalidAddress()`: Invalid address (zero address) provided

### Filecoin Warm Storage Service (FWSS) Contract Interface

**Method**: `settleFilBeamPaymentRails(uint256 dataSetId, uint256 cdnAmount, uint256 cacheMissAmount)`
- **Purpose**: Settle CDN or cache-miss payment rails based on calculated amounts
- **Access**: Callable only by FilBeamOperator contract
- **Parameters**: Either cdnAmount or cacheMissAmount will be zero depending on settlement type

**Method**: `terminateCDNPaymentRails(uint256 dataSetId)`
- **Purpose**: Terminate CDN payment rails for a specific dataset
- **Access**: Callable only by FilBeamOperator contract

### Key Implementation Features

#### Rate-Based Settlement
- Immutable rates per byte for both CDN and cache-miss usage set at contract deployment
- Settlement amounts calculated at report time as: `usage * rate`
- Rates cannot be changed after deployment, ensuring predictable pricing

#### Independent Settlement Rails
- CDN and cache-miss settlements operate independently
- Each rail tracks its own settlement epoch
- Allows flexible settlement patterns for different stakeholders

#### Amount Accumulation
- Settlement amounts (calculated at report time) accumulate between settlements
- Only unsettled amounts are stored in contract state
- Settlement reduces accumulated amounts by settled amount (supports partial settlements)

#### Epoch Management
- Strict epoch ordering enforcement
- Prevents duplicate epoch reporting
- Supports batched reporting of multiple epochs via `recordUsageRollups` method for gas efficiency
- Independent epoch tracking per dataset

#### Payments Contract Integration
- Integrates with external Payments contract to enforce lockup limits
- Retrieves rail information including `lockupFixed` to determine maximum settleable amount
- Supports partial settlements when accumulated amount exceeds available lockup
- Gracefully handles missing or invalid rails by skipping settlement
- Multiple settlement calls may be required to fully settle large accumulated amounts
18 changes: 18 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
{
"lib/openzeppelin-contracts": {
"tag": {
"name": "v5.4.0",
"rev": "c64a1edb67b6e3f4a15cca8909c9482ad33a02b0"
}
},
"lib/filecoin-pay": {
"tag": {
"name": "v0.6.0",
"rev": "3f022bd5f3e305d32e1221551e933f662df8da18"
}
},
"lib/openzeppelin-contracts-upgradeable": {
"tag": {
"name": "v5.4.0",
"rev": "e725abddf1e01cf05ace496e950fc8e243cc7cab"
}
},
"lib/forge-std": {
"tag": {
"name": "v1.10.0",
Expand Down
Loading