Skip to content

Settle Payment Rails

This guide explains how storage providers can settle their cache-miss payment rails to collect earnings.

For background on how the payment system works, see Payment Model.

  • Storage provider wallet with signing capability
  • Node.js with viem library installed
  • Data set IDs for which you’re the storage provider
  • tFIL for gas fees on Calibration testnet or FIL on mainnet
PropertyValue
ContractFilBeamOperator
Address0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF
MethodsettleCacheMissPaymentRails(uint256[] dataSetIds)
PropertyValue
ContractFilBeamOperator
Address0x9E90749D298C4ca43Bb468CA859Dfe167F9CdCf2
MethodsettleCacheMissPaymentRails(uint256[] dataSetIds)

The simplest way to settle payment rails is using the cast command from Foundry.

Terminal window
curl -L https://foundry.paradigm.xyz | bash
foundryup
Terminal window
# Set your private key (or use --interactive for prompt)
export PRIVATE_KEY=0x...
# Calibration testnet
cast send 0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF \
"settleCacheMissPaymentRails(uint256[])" "[12345]" \
--private-key $PRIVATE_KEY \
--rpc-url https://api.calibration.node.glif.io/rpc/v1
# Mainnet
cast send 0x9E90749D298C4ca43Bb468CA859Dfe167F9CdCf2 \
"settleCacheMissPaymentRails(uint256[])" "[12345]" \
--private-key $PRIVATE_KEY \
--rpc-url https://api.node.glif.io/rpc/v1
Terminal window
# Batch multiple data sets in one transaction
cast send 0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF \
"settleCacheMissPaymentRails(uint256[])" "[12345,67890,11111]" \
--private-key $PRIVATE_KEY \
--rpc-url https://api.calibration.node.glif.io/rpc/v1

Use cast call to simulate without spending gas:

Terminal window
cast call 0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF \
"settleCacheMissPaymentRails(uint256[])" "[12345]" \
--rpc-url https://api.calibration.node.glif.io/rpc/v1
Terminal window
cast send 0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF \
"settleCacheMissPaymentRails(uint256[])" "[12345]" \
--ledger \
--rpc-url https://api.calibration.node.glif.io/rpc/v1

Install dependencies:

Terminal window
npm install viem
import { createWalletClient, createPublicClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { filecoinCalibration } from 'viem/chains'
// Your storage provider private key
const account = privateKeyToAccount(process.env.SP_PRIVATE_KEY)
// Create clients
const publicClient = createPublicClient({
chain: filecoinCalibration,
transport: http()
})
const walletClient = createWalletClient({
account,
chain: filecoinCalibration,
transport: http()
})
console.log('Storage Provider Address:', account.address)
const FilBeamOperatorABI = [
{
type: 'function',
name: 'settleCacheMissPaymentRails',
inputs: [
{
name: 'dataSetIds',
type: 'uint256[]',
internalType: 'uint256[]'
}
],
outputs: [],
stateMutability: 'nonpayable'
}
]
// Contract addresses
const FILBEAM_OPERATOR_CALIBRATION = '0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF'
const FILBEAM_OPERATOR_MAINNET = '0x9E90749D298C4ca43Bb468CA859Dfe167F9CdCf2'
async function settleCacheMissPaymentRails(dataSetIds) {
console.log(`Settling payment rails for ${dataSetIds.length} data sets`)
// Simulate the transaction first
const { request } = await publicClient.simulateContract({
account,
abi: FilBeamOperatorABI,
address: FILBEAM_OPERATOR_CALIBRATION,
functionName: 'settleCacheMissPaymentRails',
args: [dataSetIds.map(id => BigInt(id))]
})
// Execute the transaction
const hash = await walletClient.writeContract(request)
console.log('Transaction submitted:', hash)
// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log('Transaction confirmed in block:', receipt.blockNumber)
return receipt
}
// Usage
const dataSetIds = ['12345', '67890', '11111']
await settleCacheMissPaymentRails(dataSetIds)
import { createWalletClient, createPublicClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { filecoinCalibration } from 'viem/chains'
// Configuration
const SP_PRIVATE_KEY = process.env.SP_PRIVATE_KEY
const FILBEAM_OPERATOR_ADDRESS = '0x5991E4F9fcEF4AE23959eE03638B4688A7e1EcfF' // Calibration
const FilBeamOperatorABI = [
{
type: 'function',
name: 'settleCacheMissPaymentRails',
inputs: [{ name: 'dataSetIds', type: 'uint256[]' }],
outputs: [],
stateMutability: 'nonpayable'
}
]
async function main() {
// Setup
const account = privateKeyToAccount(SP_PRIVATE_KEY)
const publicClient = createPublicClient({
chain: filecoinCalibration,
transport: http()
})
const walletClient = createWalletClient({
account,
chain: filecoinCalibration,
transport: http()
})
console.log('=== FilBeam Payment Rail Settlement ===')
console.log('Storage Provider:', account.address)
// Data sets to settle (replace with your actual data set IDs)
const dataSetIds = [
'12345',
'67890'
]
if (dataSetIds.length === 0) {
console.log('No data sets to settle')
return
}
console.log(`\nSettling ${dataSetIds.length} data sets:`, dataSetIds)
try {
// Simulate first to catch errors
console.log('\nSimulating transaction...')
const { request } = await publicClient.simulateContract({
account,
abi: FilBeamOperatorABI,
address: FILBEAM_OPERATOR_ADDRESS,
functionName: 'settleCacheMissPaymentRails',
args: [dataSetIds.map(id => BigInt(id))]
})
console.log('Simulation successful, submitting transaction...')
// Submit transaction
const hash = await walletClient.writeContract(request)
console.log('Transaction hash:', hash)
// Wait for confirmation
console.log('Waiting for confirmation...')
const receipt = await publicClient.waitForTransactionReceipt({
hash,
timeout: 120_000 // 2 minute timeout
})
if (receipt.status === 'success') {
console.log('\nSettlement successful!')
console.log('Block number:', receipt.blockNumber)
console.log('Gas used:', receipt.gasUsed)
} else {
console.log('\nTransaction failed')
}
} catch (error) {
console.error('\nSettlement failed:', error.message)
if (error.message.includes('insufficient funds')) {
console.log('Hint: You need more tFIL for gas fees')
}
}
}
main().catch(console.error)

We recommend settling payment rails at least once per week. More frequent settlements incur unnecessary gas fees, while less frequent settlements create a risk that payers can claim their lockup after terminating a payment rail.

Batch multiple data sets in a single transaction:

// Good: Batch settlement (one transaction)
await settleCacheMissPaymentRails(['12345', '67890', '11111'])
// Avoid: Individual settlements (multiple transactions = more gas)
await settleCacheMissPaymentRails(['12345'])
await settleCacheMissPaymentRails(['67890'])
await settleCacheMissPaymentRails(['11111'])

Track your settlements:

async function logSettlement(dataSetIds, receipt) {
const log = {
timestamp: new Date().toISOString(),
dataSetIds,
transactionHash: receipt.transactionHash,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString(),
status: receipt.status
}
// Save to file or database
console.log('Settlement log:', JSON.stringify(log, null, 2))
}
  • Check that the data sets have active payment rails with accumulated funds
  • Verify the contract address is correct
  • Ensure the data sets exist and have CDN enabled

Note: The settlement function is public - anyone can call it. However, the funds always flow to the designated storage provider, so there’s no benefit to others calling it.

You’ll need tFIL (calibration) or FIL (mainnet) for gas fees. On testnet, claim tFIL from the Calibration faucet. On mainnet, acquire FIL through a centralized exchange (Coinbase, Binance) or swap via Squidrouter.

  • Verify the data set ID is correct
  • Ensure the data set has CDN enabled
  • Check that you’re the registered storage provider