Skip to content

Payment Rails

This reference documents how payment rails work in FilBeam as part of the Filecoin Pay system.

A payment rail is a payment channel between a payer and recipient with configurable terms. Rails enable automated, continuous token transfers with built-in protections through lockup mechanisms.

In FilBeam, payment rails guarantee that:

  • Storage providers get paid for retrieval services
  • FilBeam gets paid for CDN delivery
  • Users can’t consume services without sufficient funds

When a data set is created with FilBeam enabled, three payment rails are created:

RailPayerRecipientPricingPurpose
StorageUserStorage ProviderTime-basedOngoing storage costs
CDN EgressUserFilBeamUsage-based ($7/TiB)CDN delivery fees
Cache-Miss EgressUserStorage ProviderUsage-based ($7/TiB)Retrieval from origin
flowchart LR
    subgraph User["User Filecoin Pay Account"]
        Funds[Deposited USDFC]
    end

    subgraph Rails["Payment Rails"]
        SR[Storage Rail]
        CR[CDN Rail]
        MR[Cache-Miss Rail]
    end

    Funds --> SR --> SP1[Storage Provider]
    Funds --> CR --> FB[FilBeam]
    Funds --> MR --> SP2[Storage Provider]

Rails use lockup to guarantee funds are available for services:

  1. Streaming Lockup (for storage): paymentRate × lockupPeriod

    • Guarantees funds for rate-based payments over a pre-agreed period
    • Used for ongoing storage costs
  2. Fixed Lockup (for egress): Reserved for egress payments

    • Used for CDN and cache-miss egress
    • Converted to quota in FilBeam’s off-chain database

The lockup is a safety mechanism, not a pre-payment. During normal operations, payments draw from general funds. After termination, locked funds become available for final settlement.

Settlement transfers earned funds from the payment rail fixed lockup to the recipient’s Filecoin Pay account:

sequenceDiagram
    participant SP as Storage Provider
    participant SPA as SP Filecoin Pay Account
    participant FBO as FilBeamOperator
    participant FP as Filecoin Pay
    participant Payer as Payment Rail Lockup

    SP->>FBO: settleCacheMissPaymentRails([dataSetIds])
    FBO->>FBO: Get pre-calculated amount
    FBO->>FP: settleRail(railId, amount)
    FP->>Payer: Deduct from lockup
    FP->>SPA: Transfer USDFC

Key points:

  • Settlement amounts are pre-calculated during usage reporting
  • Anyone can trigger settlement, but funds go to the designated recipient
  • Partial settlements are supported if funds are insufficient

Rails are created automatically when a data set is created with FilBeam enabled:

sequenceDiagram
    participant User
    participant FWSS as FWSS Contract
    participant FP as Filecoin Pay

    User->>FWSS: Create data set (withCDN: true)
    FWSS->>FP: createRail (storage)
    FWSS->>FP: createRail (CDN egress)
    FWSS->>FP: createRail (cache-miss egress)
    FWSS->>FP: Lock initial funds in CDN and cache-miss payment rail

Users can add more funds to egress rails:

import { Synapse, WarmStorageService, RPC_URLS } from '@filoz/synapse-sdk'
import { ethers } from 'ethers'
const synapse = await Synapse.create({
privateKey: process.env.PRIVATE_KEY,
rpcURL: RPC_URLS.calibration.http
})
const warmStorage = await WarmStorageService.create(synapse.getProvider(), synapse.getWarmStorageAddress())
const dataSetId = 3830
// Top up with $10 for CDN and $10 for cache-miss
const cdnAmount = ethers.parseUnits('10', 18)
const cacheMissAmount = ethers.parseUnits('10', 18)
const tx = await warmStorageService.topUpCDNPaymentRails(synapse.getSigner(), dataSetId, cdnAmount, cacheMissAmount)

This locks additional USDFC in the payment rails and emits a CdnPaymentRailsToppedUp event.

As content is served, FilBeam tracks usage off-chain and periodically reports it on-chain:

sequenceDiagram
    participant FB as FilBeam
    participant FBO as FilBeamOperator
    participant FP as Filecoin Pay

    Note over FB: Aggregate retrieval logs
    FB->>FBO: recordUsageRollups(epoch, dataSetIds, cdnBytes, cacheMissBytes)
    FBO->>FBO: Calculate settlement amounts (bytes × rate)

    Note over FB: Later, settle payments
    FB->>FBO: settleCDNPaymentRails([dataSetIds])
    FBO->>FP: Settle CDN rail
    FP->>FB: Transfer USDFC

Storage providers can settle their cache-miss earnings:

await walletClient.writeContract({
address: filBeamOperator,
abi: filBeamOperatorAbi,
functionName: 'settleCacheMissPaymentRails',
args: [dataSetIds]
})

Users can terminate FilBeam by terminating the entire data set:

Terminate entire service:

sequenceDiagram
    participant User
    participant FWSS as FWSS Contract
    participant FP as Filecoin Pay

    User->>FWSS: terminateService(dataSetId)
    FWSS->>FP: terminateRail (storage)
    FWSS->>FP: terminateRail (CDN egress)
    FWSS->>FP: terminateRail (cache-miss egress)

After termination:

  • No content from terminated data set can be served via FilBeam
  • Outstanding usage can still be settled
  • Remaining locked funds are released after the lockup period

FilBeam converts rail lockup to quota for fast request validation:

ConceptLocationPurpose
LockupOn-chain (Filecoin Pay)Guarantees funds are reserved
QuotaOff-chain (FilBeam DB)Fast request validation

When you top up:

  1. USDFC is locked in the payment rail (on-chain)
  2. FilBeam calculates quota from the locked amount (off-chain)
  3. Quota is decremented as content is served (off-chain)
  4. Usage is reported and settled periodically (on-chain)
Quota (bytes) = (Locked USDFC × BYTES_PER_TIB) / RATE_PER_TIB
Where:
- BYTES_PER_TIB = 1,099,511,627,776 (1024^4)
- RATE_PER_TIB = 7 × 10^18 (USDFC with 18 decimals = $7)

Before topping up rails, ensure you have sufficient USDFC deposited:

import { Synapse, WarmStorageService, RPC_URLS, TOKENS } from '@filoz/synapse-sdk'
import { ethers } from 'ethers'
const synapse = await Synapse.create({
privateKey: process.env.PRIVATE_KEY,
rpcURL: RPC_URLS.calibration.http
})
const accountInfo = await synapse.payments.accountInfo(TOKENS.USDFC)
console.log('Available funds:', accountInfo.availableFunds)
console.log('Current lockup:', accountInfo.lockupCurrent)

Unlocked funds can be withdrawn:

await synapse.payments.withdraw(amount, TOKENS.USDFC)

Note: Funds locked in active payment rails cannot be withdrawn until the rail is terminated and the lockup period expires.

EventContractDescription
CdnPaymentRailsToppedUpFWSSFunds added to CDN and cache-miss rails
CdnServiceTerminatedFWSSFilBeam service ended for data set
ServiceTerminatedFWSSEntire data set service ended
ErrorCauseSolution
Insufficient fundsNot enough USDFC depositedDeposit more USDFC to your account
Insufficient unlocked fundsAll funds locked in railsWait for settlement or terminate rails
Rail not foundInvalid data set IDVerify the data set exists and has FilBeam enabled
Already terminatedRail already terminatedCannot top up terminated rails