Skip to main content

Architecture

Filecoin Onchain Cloud consists of several interrelated smart contracts:

Contract Responsibilities

Filecoin Pay

Generic payment rails for continuous rate-based payments

Warm Storage

Storage service with PDP verification and payment integration

PDP Verifier

Neutral proof verification without business logic

SP Registry

Provider registration and discovery

Session Keys

Disposable key authorization for dapps

Data Flow

Upload Flow

1
Client Signs EIP-712
2
Client creates an EIP-712 signed message for the FWSS contract:
3
import { signCreateDataSet } from '@filoz/synapse-core/typed-data'

const signature = await signCreateDataSet(client, {
  dataSetInfo: {
    client: client.account.address,
    payer: client.account.address,
    serviceProvider: provider.serviceProvider,
    serviceProviderId: provider.id,
    startEpoch: currentEpoch,
    rate: estimatedRate,
    lockup: estimatedLockup,
    metadata: [],
  },
  nonce: currentNonce,
})
4
Curio HTTP API
5
Storage provider’s Curio instance receives the signed message and data.
6
PDP Verifier Contract
7
Curio calls PDPVerifier with the signature in extraData:
8
function addPieces(
    uint256 dataSetId,
    PieceInfo[] calldata pieces,
    bytes calldata extraData
) external;
9
FWSS Callback
10
PDPVerifier delegates to FWSS via callback:
11
function beforeAddPieces(
    uint256 dataSetId,
    PieceInfo[] calldata pieces,
    bytes calldata extraData
) external returns (bytes32);
12
Payment Rails
13
FWSS validates signature and creates/updates payment rail in Filecoin Pay.

Proof Flow

1
Periodic Proofs
2
Storage provider submits PDP proofs periodically to PDPVerifier.
3
Validation
4
PDPVerifier validates the cryptographic proofs.
5
Settlement
6
On successful proof, payment rail settles funds from client to provider.
7
Failure Handling
8
Failed proofs result in penalty or data set termination.

Contract Addresses

Mainnet

Contract addresses are auto-discovered from the network. Query via:
import { mainnet } from '@filoz/synapse-core/chains'

console.log('FWSS:', mainnet.contracts.fwss.address)
console.log('Payments:', mainnet.contracts.filecoinPay.address)
console.log('PDP Verifier:', mainnet.contracts.pdpVerifier.address)
console.log('SP Registry:', mainnet.contracts.spRegistry.address)

Calibration Testnet

import { calibration } from '@filoz/synapse-core/chains'

console.log('FWSS:', calibration.contracts.fwss.address)
console.log('Payments:', calibration.contracts.filecoinPay.address)

EIP-712 Signing

All contract interactions use EIP-712 typed data for security:
import * as TypedData from '@filoz/synapse-core/typed-data'

// Create data set
const createSig = await TypedData.signCreateDataSet(client, { dataSetInfo, nonce })

// Add pieces
const addSig = await TypedData.signAddPieces(client, { addPiecesInfo, nonce })

// Delete data set
const deleteSig = await TypedData.signDeleteDataSet(client, { deleteInfo, nonce })

Contract ABIs

All ABIs are available in @filoz/synapse-core:
import { calibration } from '@filoz/synapse-core/chains'

const fwssAbi = calibration.contracts.fwss.abi
const payAbi = calibration.contracts.filecoinPay.abi
const pdpAbi = calibration.contracts.pdpVerifier.abi

Read vs Write Operations

Read Operations

Query contract state without transactions:
import * as WarmStorage from '@filoz/synapse-core/warm-storage'
import { createPublicClient, http } from 'viem'

const client = createPublicClient({
  chain: calibration,
  transport: http(),
})

// Get service price
const price = await WarmStorage.getServicePrice(client)

// Get approved providers
const providers = await WarmStorage.getApprovedProviders(client)

// Get data set info
const dataSet = await WarmStorage.getDataSet(client, { dataSetId: 123n })

Write Operations

Send transactions to modify state:
import * as WarmStorage from '@filoz/synapse-core/warm-storage'
import { createWalletClient, http } from 'viem'

const client = createWalletClient({
  chain: calibration,
  transport: http(),
  account,
})

// Terminate data set
const hash = await WarmStorage.terminateDataSet(client, { dataSetId: 123n })

// Wait for confirmation
const receipt = await client.waitForTransactionReceipt({ hash })

Event Listening

import { watchContractEvent } from 'viem/actions'

const unwatch = watchContractEvent(client, {
  address: chain.contracts.fwss.address,
  abi: chain.contracts.fwss.abi,
  eventName: 'DataSetCreated',
  onLogs: (logs) => {
    for (const log of logs) {
      console.log('Data set created:', log.args.dataSetId)
    }
  },
})

// Stop watching
unwatch()

Multicall

Batch multiple read operations:
import { multicall } from 'viem/actions'
import * as WarmStorage from '@filoz/synapse-core/warm-storage'
import * as Pay from '@filoz/synapse-core/pay'

const results = await multicall(client, {
  contracts: [
    WarmStorage.getServicePriceCall({ chain: calibration }),
    WarmStorage.getApprovedProvidersCall({ chain: calibration }),
    Pay.accountsCall({ chain: calibration, address: account.address }),
  ],
})

const [priceResult, providersResult, accountResult] = results

Gas Estimation

import { estimateContractGas } from 'viem/actions'
import * as WarmStorage from '@filoz/synapse-core/warm-storage'

const gas = await estimateContractGas(
  client,
  WarmStorage.terminateDataSetCall({
    chain: calibration,
    dataSetId: 123n,
  })
)

console.log(`Estimated gas: ${gas}`)

Contract Simulation

import { simulateContract } from 'viem/actions'

const { result, request } = await simulateContract(
  client,
  WarmStorage.terminateDataSetCall({
    chain: calibration,
    dataSetId: 123n,
  })
)

console.log('Simulation result:', result)

// If simulation succeeds, execute
const hash = await writeContract(client, request)

Error Handling

import { BaseError, ContractFunctionRevertedError } from 'viem'

try {
  await WarmStorage.terminateDataSet(client, { dataSetId: 123n })
} catch (error) {
  if (error instanceof BaseError) {
    const revertError = error.walk(
      err => err instanceof ContractFunctionRevertedError
    )
    
    if (revertError instanceof ContractFunctionRevertedError) {
      const errorName = revertError.data?.errorName
      console.error('Contract revert:', errorName)
      
      // Handle specific errors
      if (errorName === 'NotAuthorized') {
        console.error('You do not own this data set')
      }
    }
  }
}

Network Configuration

import { mainnet, calibration } from '@filoz/synapse-core/chains'

// Mainnet (production)
console.log('Chain ID:', mainnet.id)
console.log('RPC:', mainnet.rpcUrls.default.http[0])
console.log('Explorer:', mainnet.blockExplorers?.default.url)

// Calibration (testnet)
console.log('Chain ID:', calibration.id)
console.log('RPC:', calibration.rpcUrls.default.http[0])
console.log('Testnet:', calibration.testnet)

Best Practices

Use SDK

Use Synapse SDK for high-level operations

Multicall Reads

Batch multiple reads with multicall

Simulate First

Always simulate before executing writes

Handle Reverts

Parse and handle contract-specific errors

Source Code

FWSS

Filecoin Warm Storage Service contracts

Filecoin Pay

Payment rails contract

PDP Verifier

Proof verification contract

SP Registry

Service provider registry

Next Steps

FWSS Contract

Learn about the storage service contract

Filecoin Pay

Understand payment rails

PDP Verifier

Explore proof verification

Build docs developers (and LLMs) love