Skip to content

Commit 42280b7

Browse files
authored
Merge pull request #1 from filbeam/feat/filbeam-usage-based-payments-contract
feat: implement FilBeam (operator) contract
2 parents 40beff7 + 1e5f146 commit 42280b7

19 files changed

+2276
-80
lines changed

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
[submodule "lib/forge-std"]
22
path = lib/forge-std
33
url = https://github.com/foundry-rs/forge-std
4+
[submodule "lib/openzeppelin-contracts"]
5+
path = lib/openzeppelin-contracts
6+
url = https://github.com/OpenZeppelin/openzeppelin-contracts
7+
[submodule "lib/filecoin-pay"]
8+
path = lib/filecoin-pay
9+
url = https://github.com/filozone/filecoin-pay

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Bash commands
22
- `forge fmt`: Format the project
33
- `forge test`: Test the project
4+
- `forge install`: Install dependencies
45

56
# Workflow
67
- Make sure to run `forge fmt` when you're done making a series of test changes
78
- Prefer running single tests, and not the whole test suite, for performance
9+
- Update [SPEC.md](SPEC.md) and [README.md](README.md) when making changes to the contract API

README.md

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
## Foundry
1+
# FilBeamOperator Contract
2+
3+
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).
24

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

5-
Foundry consists of:
7+
- **Usage Reporting**: Batch methods for reporting CDN and cache-miss usage
8+
- **Rail Settlements**: Independent settlement for CDN and cache-miss payment rails
9+
- **Access Control**: Separate roles for contract management and usage reporting
610

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

12-
## Documentation
13+
Documentation: https://book.getfoundry.sh/
1314

14-
https://book.getfoundry.sh/
15+
## Prerequisites
16+
- [Foundry](https://getfoundry.sh/) - Ethereum development toolchain
1517

16-
## Usage
18+
### Usage
1719

1820
### Build
1921

@@ -33,24 +35,93 @@ $ forge test
3335
$ forge fmt
3436
```
3537

36-
### Gas Snapshots
38+
### Deploy FilBeamOperator Contract
3739

38-
```shell
39-
$ forge snapshot
40+
The FilBeamOperator contract requires the following constructor parameters:
41+
42+
```solidity
43+
constructor(
44+
address fwssAddress, // FWSS contract address
45+
address _paymentsAddress, // Payments contract address for rail management
46+
uint256 _cdnRatePerByte, // Rate per byte for CDN usage
47+
uint256 _cacheMissRatePerByte, // Rate per byte for cache-miss usage
48+
address _filBeamOperatorController // Address authorized to report usage
49+
)
4050
```
4151

42-
### Anvil
52+
#### Deployment Example
53+
54+
Deploy the contract using Forge script:
55+
56+
```bash
57+
PRIVATE_KEY=<deployer_private_key> \
58+
FILBEAM_CONTROLLER=<filbeam_controller_address> \
59+
PAYMENTS_ADDRESS=<filecoin_pay_contract_address> \
60+
FWSS_ADDRESS=<fwss_contract_address> \
61+
PAYMENTS_ADDRESS=<payments_contract_address> \
62+
CDN_PRICE_USD_PER_TIB=<cdn_price_usd_per_tib> \
63+
CACHE_MISS_PRICE_USD_PER_TIB=<cache_miss_price_usd_per_tib> \
64+
PRICE_DECIMALS=<price_decimals> \
65+
forge script script/DeployFilBeamOperator.s.sol \
66+
--rpc-url <your_rpc_url> \
67+
--broadcast
68+
```
4369

44-
```shell
45-
$ anvil
70+
**Note**: The deployer address automatically becomes the contract owner.
71+
72+
## Contract API
73+
74+
### Usage Reporting
75+
76+
```solidity
77+
function recordUsageRollups(
78+
uint256 toEpoch,
79+
uint256[] calldata dataSetIds,
80+
uint256[] calldata cdnBytesUsed,
81+
uint256[] calldata cacheMissBytesUsed
82+
) external onlyFilBeamOperatorController
4683
```
4784

48-
### Deploy
85+
### Settlement Operations
4986

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

92+
### Data Set Management
93+
94+
**Payment Rail Termination**
95+
```solidity
96+
function terminateCDNPaymentRails(uint256 dataSetId) external onlyFilBeamOperatorController
97+
```
98+
99+
### Contract Management
100+
101+
**Ownership & Controller**
102+
```solidity
103+
function transferOwnership(address newOwner) external onlyOwner
104+
function setFilBeamOperatorController(address _filBeamOperatorController) external onlyOwner
105+
```
106+
107+
## Key Concepts
108+
109+
### Batch Operations
110+
- **Gas Efficient**: Reduce transaction costs for bulk operations
111+
- **Atomic**: All operations in a batch succeed or all fail
112+
- **Independent Rails**: CDN and cache-miss settlements operate independently
113+
114+
### Pricing Model
115+
- **Usage-Based**: Calculated as `usage_bytes * rate_per_byte` at report time
116+
- **Immutable Rates**: Rates are set at deployment and cannot be changed, ensuring predictable pricing
117+
- **Transparent Pricing**: All users can view the fixed rates on-chain
118+
- **Partial Settlements**: Supports partial settlements when accumulated amount exceeds payment rail's `lockupFixed`
119+
120+
### Rail Settlement
121+
- **Independent Tracking**: CDN and cache-miss settlements tracked separately
122+
- **Epoch-Based**: Settlement periods defined by epoch ranges
123+
- **Accumulative**: Usage accumulates between settlements
124+
54125
### Cast
55126

56127
```shell

SPEC.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
## Specification
2+
3+
### FilBeamOperator (Operator) Contract
4+
5+
#### Overview
6+
7+
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.
8+
9+
#### Initialization
10+
**Method**: `constructor(address fwssAddress, uint256 _cdnRatePerByte, uint256 _cacheMissRatePerByte, address _filBeamOperatorController)`
11+
12+
**Parameters**:
13+
- `address fwssAddress`: Address of the FWSS contract
14+
- `uint256 _cdnRatePerByte`: Rate per byte for CDN usage billing (must be > 0)
15+
- `uint256 _cacheMissRatePerByte`: Rate per byte for cache-miss usage billing (must be > 0)
16+
- `address _filBeamOperatorController`: Address authorized to report usage and terminate payment rails
17+
18+
**Owner**:
19+
- The deployer (msg.sender) automatically becomes the contract owner
20+
21+
**Validations**:
22+
- FWSS address cannot be zero address
23+
- Both rates must be greater than zero
24+
- FilBeamOperator controller cannot be zero address
25+
26+
#### Data Structure
27+
**DataSetUsage Struct**:
28+
- `uint256 cdnAmount`: Accumulated CDN settlement amount between settlements (calculated at report time)
29+
- `uint256 cacheMissAmount`: Accumulated cache-miss settlement amount between settlements (calculated at report time)
30+
- `uint256 maxReportedEpoch`: Highest epoch number reported for this dataset (0 indicates uninitialized dataset)
31+
- `uint256 lastCDNSettlementEpoch`: Last epoch settled for CDN payment rail
32+
- `uint256 lastCacheMissSettlementEpoch`: Last epoch settled for cache-miss payment rail
33+
34+
#### Usage Reporting
35+
36+
**Method**: `recordUsageRollups(uint256 toEpoch, uint256[] dataSetIds, uint256[] cdnBytesUsed, uint256[] cacheMissBytesUsed)`
37+
38+
- **Access**: FilBeamOperator controller only
39+
- **Purpose**: Accepts multiple usage reports in a single transaction for improved gas efficiency
40+
- **toEpoch Parameter**: Single epoch number up to which usage is reported for all datasets in the batch
41+
- **Epoch Requirements**:
42+
- Epoch must be > 0
43+
- Epoch must be greater than previously reported epochs for each dataset
44+
- Each epoch can only be reported once per dataset
45+
- **Usage Requirements**:
46+
- Usage is converted to settlement amounts using current rates at report time
47+
- Amounts accumulate in the dataset between settlements
48+
- **Parameter Requirements**:
49+
- All arrays must have equal length
50+
- All datasets in the batch report usage up to the same toEpoch
51+
- **Batch Processing**:
52+
- Processes all reports atomically (all succeed or all fail)
53+
- Maintains epoch ordering and validation rules per dataset
54+
- Prevents duplicate epoch reporting within the batch
55+
- **State Updates**:
56+
- Initialize dataset on first report (sets maxReportedEpoch to non-zero value)
57+
- Calculate amounts: `cdnAmount = cdnBytesUsed * cdnRatePerByte`, `cacheMissAmount = cacheMissBytesUsed * cacheMissRatePerByte`
58+
- Accumulate calculated amounts
59+
- Update max reported epoch to toEpoch
60+
- **Events**: Emits individual `UsageReported` event for each processed report (contains bytes, not amounts)
61+
62+
#### Payment Rail Settlement
63+
64+
**Method**: `settleCDNPaymentRails(uint256[] dataSetIds)`
65+
66+
- **Access**: Publicly callable (anyone can trigger settlement)
67+
- **Purpose**: Settles CDN payment rails for multiple datasets in a single transaction
68+
- **Calculation Period**: From last CDN settlement epoch + 1 to max reported epoch
69+
- **Settlement Logic**:
70+
- Retrieves rail ID from FWSS DataSetInfo
71+
- Fetches rail details from Payments contract to get `lockupFixed`
72+
- Calculates settleable amount: `min(accumulated_amount, rail.lockupFixed)`
73+
- Only calls FWSS contract if settleable amount > 0
74+
- Reduces accumulated CDN amount by settled amount (may leave remainder)
75+
- **State Updates**:
76+
- Update last CDN settlement epoch to max reported epoch
77+
- Reduce accumulated amount by settled amount (not reset to zero if partial)
78+
- **Requirements**: None - gracefully skips datasets that cannot be settled
79+
- **Batch Processing**:
80+
- Processes each dataset independently (non-reverting)
81+
- Skips uninitialized datasets or those without new usage
82+
- Skips datasets without valid rail configuration
83+
- Continues processing even if some datasets cannot be settled
84+
- **Partial Settlement**: Supports partial settlements when `accumulated_amount > lockupFixed`
85+
- **Events**: Emits `CDNSettlement` event with actual settled amount (may be less than accumulated)
86+
- **Independent Operation**: Can be called independently of cache-miss settlement
87+
88+
**Method**: `settleCacheMissPaymentRails(uint256[] dataSetIds)`
89+
90+
- **Access**: Publicly callable (typically called by Storage Providers)
91+
- **Purpose**: Settles cache-miss payment rails for multiple datasets in a single transaction
92+
- **Calculation Period**: From last cache-miss settlement epoch + 1 to max reported epoch
93+
- **Settlement Logic**:
94+
- Retrieves rail ID from FWSS DataSetInfo
95+
- Fetches rail details from Payments contract to get `lockupFixed`
96+
- Calculates settleable amount: `min(accumulated_amount, rail.lockupFixed)`
97+
- Only calls FWSS contract if settleable amount > 0
98+
- Reduces accumulated cache-miss amount by settled amount (may leave remainder)
99+
- **State Updates**:
100+
- Update last cache-miss settlement epoch to max reported epoch
101+
- Reduce accumulated amount by settled amount (not reset to zero if partial)
102+
- **Requirements**: None - gracefully skips datasets that cannot be settled
103+
- **Batch Processing**:
104+
- Processes each dataset independently (non-reverting)
105+
- Skips uninitialized datasets or those without new usage
106+
- Skips datasets without valid rail configuration
107+
- Continues processing even if some datasets cannot be settled
108+
- **Partial Settlement**: Supports partial settlements when `accumulated_amount > lockupFixed`
109+
- **Events**: Emits `CacheMissSettlement` event with actual settled amount (may be less than accumulated)
110+
- **Independent Operation**: Can be called independently of CDN settlement
111+
112+
#### Payment Rail Termination
113+
**Method**: `terminateCDNPaymentRails(uint256 dataSetId)`
114+
115+
- **Access**: FilBeamOperator controller only
116+
- **Requirements**: Dataset must be initialized
117+
- **Process**: Forward termination call to FWSS contract
118+
- **Events**: Emits `PaymentRailsTerminated` event
119+
120+
#### Data Access
121+
**Method**: `getDataSetUsage(uint256 dataSetId)`
122+
123+
**Returns**:
124+
- `uint256 cdnAmount`: Current accumulated CDN settlement amount
125+
- `uint256 cacheMissAmount`: Current accumulated cache-miss settlement amount
126+
- `uint256 maxReportedEpoch`: Highest reported epoch (0 indicates uninitialized dataset)
127+
- `uint256 lastCDNSettlementEpoch`: Last CDN settlement epoch
128+
- `uint256 lastCacheMissSettlementEpoch`: Last cache-miss settlement epoch
129+
130+
#### Ownership Management
131+
**Method**: `transferOwnership(address newOwner)`
132+
133+
- **Access**: Contract owner only
134+
- **Requirements**: New owner cannot be zero address
135+
- **Purpose**: Transfer contract ownership
136+
137+
#### FilBeamOperator Controller Management
138+
**Method**: `setFilBeamOperatorController(address _filBeamOperatorController)`
139+
140+
- **Access**: Contract owner only
141+
- **Requirements**: FilBeamOperator controller cannot be zero address
142+
- **Purpose**: Update the authorized address for usage reporting and payment rail termination
143+
- **Events**: Emits `FilBeamOperatorControllerUpdated` event
144+
145+
#### Events
146+
- `UsageReported(uint256 indexed dataSetId, uint256 indexed fromEpoch, uint256 indexed toEpoch, uint256 cdnBytesUsed, uint256 cacheMissBytesUsed)`
147+
- `CDNSettlement(uint256 indexed dataSetId, uint256 fromEpoch, uint256 toEpoch, uint256 cdnAmount)`
148+
- `CacheMissSettlement(uint256 indexed dataSetId, uint256 fromEpoch, uint256 toEpoch, uint256 cacheMissAmount)`
149+
- `PaymentRailsTerminated(uint256 indexed dataSetId)`
150+
- `FilBeamOperatorControllerUpdated(address indexed oldController, address indexed newController)`
151+
152+
#### Access Control
153+
- **Owner**: Address authorized to manage contract ownership and set FilBeamOperator controller
154+
- **FilBeamOperator Controller**: Address authorized to report usage and terminate payment rails
155+
156+
#### Error Conditions
157+
- `OwnableUnauthorizedAccount(address)`: Caller is not the contract owner
158+
- `Unauthorized()`: Caller is not the FilBeamOperator controller
159+
- `InvalidEpoch()`: Invalid epoch number or ordering (used in usage reporting)
160+
- `InvalidUsageAmount()`: Invalid array lengths in batch operations
161+
- `InvalidRate()`: Invalid rate configuration (zero rates at deployment)
162+
- `InvalidAddress()`: Invalid address (zero address) provided
163+
164+
### Filecoin Warm Storage Service (FWSS) Contract Interface
165+
166+
**Method**: `settleFilBeamPaymentRails(uint256 dataSetId, uint256 cdnAmount, uint256 cacheMissAmount)`
167+
- **Purpose**: Settle CDN or cache-miss payment rails based on calculated amounts
168+
- **Access**: Callable only by FilBeamOperator contract
169+
- **Parameters**: Either cdnAmount or cacheMissAmount will be zero depending on settlement type
170+
171+
**Method**: `terminateCDNPaymentRails(uint256 dataSetId)`
172+
- **Purpose**: Terminate CDN payment rails for a specific dataset
173+
- **Access**: Callable only by FilBeamOperator contract
174+
175+
### Key Implementation Features
176+
177+
#### Rate-Based Settlement
178+
- Immutable rates per byte for both CDN and cache-miss usage set at contract deployment
179+
- Settlement amounts calculated at report time as: `usage * rate`
180+
- Rates cannot be changed after deployment, ensuring predictable pricing
181+
182+
#### Independent Settlement Rails
183+
- CDN and cache-miss settlements operate independently
184+
- Each rail tracks its own settlement epoch
185+
- Allows flexible settlement patterns for different stakeholders
186+
187+
#### Amount Accumulation
188+
- Settlement amounts (calculated at report time) accumulate between settlements
189+
- Only unsettled amounts are stored in contract state
190+
- Settlement reduces accumulated amounts by settled amount (supports partial settlements)
191+
192+
#### Epoch Management
193+
- Strict epoch ordering enforcement
194+
- Prevents duplicate epoch reporting
195+
- Supports batched reporting of multiple epochs via `recordUsageRollups` method for gas efficiency
196+
- Independent epoch tracking per dataset
197+
198+
#### Payments Contract Integration
199+
- Integrates with external Payments contract to enforce lockup limits
200+
- Retrieves rail information including `lockupFixed` to determine maximum settleable amount
201+
- Supports partial settlements when accumulated amount exceeds available lockup
202+
- Gracefully handles missing or invalid rails by skipping settlement
203+
- Multiple settlement calls may be required to fully settle large accumulated amounts

foundry.lock

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
{
2+
"lib/openzeppelin-contracts": {
3+
"tag": {
4+
"name": "v5.4.0",
5+
"rev": "c64a1edb67b6e3f4a15cca8909c9482ad33a02b0"
6+
}
7+
},
8+
"lib/filecoin-pay": {
9+
"tag": {
10+
"name": "v0.6.0",
11+
"rev": "3f022bd5f3e305d32e1221551e933f662df8da18"
12+
}
13+
},
14+
"lib/openzeppelin-contracts-upgradeable": {
15+
"tag": {
16+
"name": "v5.4.0",
17+
"rev": "e725abddf1e01cf05ace496e950fc8e243cc7cab"
18+
}
19+
},
220
"lib/forge-std": {
321
"tag": {
422
"name": "v1.10.0",

0 commit comments

Comments
 (0)