Skip to content

FilOzone/synapse-sdk

Repository files navigation

Synapse SDK

A JavaScript/TypeScript SDK for interacting with Filecoin Synapse - a smart-contract based marketplace for storage and other services in the Filecoin ecosystem.

Overview

The Synapse SDK is designed with flexibility in mind:

  • 🚀 Recommended Usage: Use the high-level Synapse class for a streamlined experience with sensible defaults
  • đź”§ Composable Components: Import and use individual components for fine-grained control over specific functionality

Whether you're building a quick prototype or a complex application with specific requirements, the SDK adapts to your needs.

Installation

npm install @filoz/synapse-sdk ethers

Note: ethers v6 is a peer dependency and must be installed separately.

Table of Contents


Recommended Usage

The Synapse class provides a complete, easy-to-use interface for interacting with Filecoin storage services.

Quick Start

import { Synapse, RPC_URLS, TOKENS, CONTRACT_ADDRESSES } from '@filoz/synapse-sdk'

// Initialize with private key
const synapse = await Synapse.create({
  privateKey: '0x...',
  rpcURL: RPC_URLS.mainnet.websocket
})

// Check balance of USDFC in the payments contract
const paymentsBalance = await synapse.payments.balance(TOKENS.USDFC)
console.log('USDFC balance in payments contract:',
  ethers.formatUnits(paymentsBalance, synapse.payments.decimals(TOKENS.USDFC)))

// Deposit funds for storage operations
const amount = ethers.parseUnits('10', synapse.payments.decimals(TOKENS.USDFC))
const depositTx = await synapse.payments.deposit(amount, TOKENS.USDFC)
console.log(`Deposit transaction: ${depositTx.hash}`)
// Optional: wait for confirmation
const receipt = await depositTx.wait()
console.log(`Deposit confirmed in block ${receipt.blockNumber}`)

// Approve service for creating payment rails between you and storage providers
// which are paid out over time as you use their storage
const approveTx = await synapse.payments.approveService(
  serviceAddress,
  // 10 USDFC per epoch rate allowance
  ethers.parseUnits('10', synapse.payments.decimals(TOKENS.USDFC)),
  // 1000 USDFC lockup allowance
  ethers.parseUnits('1000', synapse.payments.decimals(TOKENS.USDFC))
)
console.log(`Service approval transaction: ${approveTx.hash}`)
await approveTx.wait() // Wait for confirmation before proceeding to use the funds

// Create storage service and upload data
const storage = await synapse.createStorage()

// Upload data
const uploadResult = await storage.upload(
  new TextEncoder().encode('🚀 Welcome to decentralized storage on Filecoin! Your data is safe here. 🌍')
)
console.log(`Upload complete! CommP: ${uploadResult.commp}`)
console.log(`Size: ${uploadResult.size} bytes`)

// Download data
const data = await storage.download(uploadResult.commp)
console.log(new TextDecoder().decode(data))

With MetaMask

import { Synapse } from '@filoz/synapse-sdk'
import { ethers } from 'ethers'

// Connect to MetaMask
const provider = new ethers.BrowserProvider(window.ethereum)
const synapse = await Synapse.create({ provider })

// Same API as above
const paymentsBalance = await synapse.payments.balance(TOKENS.USDFC)

Advanced Payment Control

For users who need fine-grained control over token approvals:

import { Synapse, TOKENS, CONTRACT_ADDRESSES } from '@filoz/synapse-sdk'

const synapse = await Synapse.create({ provider })

// Check current allowance
const paymentsContract = CONTRACT_ADDRESSES.PAYMENTS[synapse.getNetwork()]
const currentAllowance = await synapse.payments.allowance(TOKENS.USDFC, paymentsContract)

// Approve only if needed
if (currentAllowance < requiredAmount) {
  const approveTx = await synapse.payments.approve(TOKENS.USDFC, paymentsContract, requiredAmount)
  console.log(`Approval transaction: ${approveTx.hash}`)
  await approveTx.wait() // Wait for approval before depositing
}

// Now deposit with optional callbacks for visibility
const depositTx = await synapse.payments.deposit(requiredAmount, TOKENS.USDFC, {
  onAllowanceCheck: (current, required) => {
    console.log(`Current allowance: ${current}, Required: ${required}`)
  },
  onApprovalTransaction: (tx) => {
    console.log(`Auto-approval sent: ${tx.hash}`)
  },
  onDepositStarting: () => {
    console.log('Starting deposit transaction...')
  }
})
console.log(`Deposit transaction: ${depositTx.hash}`)
await depositTx.wait()

// Service operator approvals (required before creating proof sets)
// Get the Pandora service address for the current network
const pandoraAddress = CONTRACT_ADDRESSES.PANDORA_SERVICE[synapse.getNetwork()]

// Approve service to create payment rails on your behalf
const serviceApproveTx = await synapse.payments.approveService(
  pandoraAddress,
  // 10 USDFC per epoch rate allowance
  ethers.parseUnits('10', synapse.payments.decimals(TOKENS.USDFC)),
  // 1000 USDFC lockup allowance
  ethers.parseUnits('1000', synapse.payments.decimals(TOKENS.USDFC))
)
console.log(`Service approval transaction: ${serviceApproveTx.hash}`)
await serviceApproveTx.wait()

// Check service approval status
const serviceStatus = await synapse.payments.serviceApproval(pandoraAddress)
console.log('Service approved:', serviceStatus.isApproved)
console.log('Rate allowance:', serviceStatus.rateAllowance)
console.log('Rate used:', serviceStatus.rateUsed)

// Revoke service if needed
const revokeTx = await synapse.payments.revokeService(pandoraAddress)
console.log(`Revoke transaction: ${revokeTx.hash}`)
await revokeTx.wait()

API Reference

Constructor Options

interface SynapseOptions {
  // Wallet Configuration (exactly one required)
  privateKey?: string           // Private key for signing
  provider?: ethers.Provider    // Browser provider (MetaMask, etc.)
  signer?: ethers.Signer        // External signer

  // Network Configuration
  rpcURL?: string              // RPC endpoint URL
  authorization?: string        // Authorization header (e.g., 'Bearer TOKEN')

  // Advanced Configuration
  disableNonceManager?: boolean // Disable automatic nonce management
  withCDN?: boolean             // Enable CDN for retrievals
  pandoraAddress?: string       // Override Pandora service contract address
}

Synapse Methods

  • payments - Access payment-related functionality (see below)
  • createStorage(options?) - Create a storage service instance (see Storage Service Creation)
  • getNetwork() - Get the network this instance is connected to ('mainnet' or 'calibration')

Synapse.payments Methods

Balance Operations:

  • walletBalance(token?) - Get wallet balance (FIL or USDFC)
  • balance() - Get available USDFC balance in payments contract (accounting for lockups)
  • accountInfo() - Get detailed USDFC account info including funds, lockup details, and available balance
  • getCurrentEpoch() - Get the current Filecoin epoch number
  • decimals() - Get token decimals (always returns 18)

Note: Currently only USDFC token is supported for payments contract operations. FIL is also supported for walletBalance().

Token Operations:

  • deposit(amount, token?, callbacks?) - Deposit funds to payments contract (handles approval automatically), returns TransactionResponse
  • withdraw(amount, token?) - Withdraw funds from payments contract, returns TransactionResponse
  • approve(token, spender, amount) - Approve token spending (for manual control), returns TransactionResponse
  • allowance(token, spender) - Check current token allowance

Service Approvals:

  • approveService(service, rateAllowance, lockupAllowance, token?) - Approve a service contract as operator, returns TransactionResponse
  • revokeService(service, token?) - Revoke service operator approval, returns TransactionResponse
  • serviceApproval(service, token?) - Check service approval status and allowances

Storage Service Creation

The SDK automatically handles all the complexity of storage setup for you - selecting providers, managing proof sets, and coordinating with the blockchain. You just call createStorage() and the SDK takes care of everything.

Behind the scenes, the process may be:

  • Fast (<1 second): When reusing existing infrastructure
  • Slower (2-5 minutes): When setting up new blockchain infrastructure

Basic Usage

// Simple creation with default provider selection
const storage = await synapse.createStorage()

Advanced Usage with Callbacks

Monitor the creation process with detailed callbacks:

const storage = await synapse.createStorage({
  providerId: 1,    // Optional: use specific provider ID
  withCDN: true,    // Optional: enable CDN for faster downloads
  callbacks: {
    // Called when a provider is selected
    onProviderSelected: (provider) => {
      console.log(`Selected provider: ${provider.owner}`)
      console.log(`  PDP URL: ${provider.pdpUrl}`)
    },

    // Called when proof set is found or created
    onProofSetResolved: (info) => {
      if (info.isExisting) {
        console.log(`Using existing proof set: ${info.proofSetId}`)
      } else {
        console.log(`Created new proof set: ${info.proofSetId}`)
      }
    },

    // Only called when creating a new proof set
    onProofSetCreationStarted: (transaction, statusUrl) => {
      console.log(`Creation transaction: ${transaction.hash}`)
      if (statusUrl) {
        console.log(`Monitor status at: ${statusUrl}`)
      }
    },

    // Progress updates during proof set creation
    onProofSetCreationProgress: (status) => {
      const elapsed = Math.round(status.elapsedMs / 1000)
      console.log(`[${elapsed}s] Mining: ${status.transactionMined}, Live: ${status.proofSetLive}`)
    }
  }
})

Creation Options

interface StorageServiceOptions {
  providerId?: number                      // Specific provider ID to use
  providerAddress?: string                 // Specific provider address to use
  proofSetId?: number                      // Specific proof set ID to use
  withCDN?: boolean                        // Enable CDN services
  callbacks?: StorageCreationCallbacks     // Progress callbacks
}

Storage Service Properties

Once created, the storage service provides access to:

// The proof set ID being used
console.log(`Proof set ID: ${storage.proofSetId}`)

// The storage provider address
console.log(`Storage provider: ${storage.storageProvider}`)

Storage Service Methods

Preflight Upload

Check if an upload is possible before attempting it:

const preflight = await storage.preflightUpload(dataSize)
console.log('Estimated costs:', preflight.estimatedCost)
console.log('Allowance sufficient:', preflight.allowanceCheck.sufficient)
Upload and Download

Upload and download data with the storage service:

// Upload with progress callbacks
const result = await storage.upload(data, {
  onUploadComplete: (commp) => {
    console.log(`Upload complete! CommP: ${commp}`)
  },
  onRootAdded: () => {
    console.log('Data added to proof set')
  }
})

// Download data
const downloaded = await storage.download(result.commp)
Size Constraints

The storage service enforces the following size limits for uploads:

  • Minimum: 65 bytes
  • Maximum: 200 MiB (209,715,200 bytes)

Attempting to upload data outside these limits will result in an error.


Using Individual Components

All components can be imported and used independently for advanced use cases. The SDK is organized to match the external service structure:

Payments Service

Direct interface to the Payments contract for token operations and operator approvals.

import { PaymentsService } from '@filoz/synapse-sdk/payments'
import { ethers } from 'ethers'

const provider = new ethers.JsonRpcProvider(rpcUrl)
const signer = await provider.getSigner()
const paymentsService = new PaymentsService(provider, signer, 'calibration', false)

// Deposit USDFC to payments contract
const depositTx = await paymentsService.deposit(amount) // amount in base units
console.log(`Deposit transaction: ${depositTx.hash}`)
await depositTx.wait() // Wait for confirmation

// Check account info
const info = await paymentsService.accountInfo() // Uses USDFC by default
console.log('Available funds:', info.availableFunds)

// Approve service as operator
const approveTx = await paymentsService.approveService(
  serviceAddress,         // e.g., Pandora contract address
  rateAllowance,         // per-epoch rate allowance in base units
  lockupAllowance        // total lockup allowance in base units
)
console.log(`Service approval transaction: ${approveTx.hash}`)
await approveTx.wait() // Wait for confirmation

Pandora Service

Interact with the Pandora contract for proof set management, storage provider operations, and storage cost calculations.

import { PandoraService } from '@filoz/synapse-sdk/pandora'

const pandoraService = new PandoraService(provider, pandoraAddress)

// Storage cost calculations
const costs = await pandoraService.calculateStorageCost(sizeInBytes)
console.log(`Storage cost: ${costs.perMonth} per month`)

// Check allowances for storage (returns allowance details and costs)
const check = await pandoraService.checkAllowanceForStorage(
  sizeInBytes,
  withCDN,
  paymentsService  // Pass PaymentsService instance
)
// check.sufficient - boolean indicating if allowances are sufficient
// check.costs - storage costs per epoch/day/month

// Prepare storage upload
const prep = await pandoraService.prepareStorageUpload({
  dataSize: sizeInBytes,
  withCDN: false
}, paymentsService)

// Get client proof sets with enhanced details
const proofSets = await pandoraService.getClientProofSetsWithDetails(clientAddress)
for (const ps of proofSets) {
  console.log(`Rail ID: ${ps.railId}, PDP Verifier ID: ${ps.pdpVerifierProofSetId}`)
  console.log(`Is Live: ${ps.isLive}, Is Managed: ${ps.isManaged}`)
  console.log(`Next Root ID: ${ps.nextRootId}`)
}

// Get only proof sets managed by this Pandora instance
const managedSets = await pandoraService.getManagedProofSets(clientAddress)

// Verify proof set creation
const verification = await pandoraService.verifyProofSetCreation(txHash)
if (verification.proofSetLive) {
  console.log(`Proof set ${verification.proofSetId} is live!`)
}

// Storage provider operations
const isApproved = await pandoraService.isProviderApproved(providerAddress)
const providers = await pandoraService.getAllApprovedProviders()

PDP Components

The PDP (Proof of Data Possession) system has three main components:

PDP Verifier

Low-level interface to the PDPVerifier contract for protocol operations.

import { PDPVerifier } from '@filoz/synapse-sdk/pdp'

const pdpVerifier = new PDPVerifier(provider)

// Check if proof set is live
const isLive = await pdpVerifier.proofSetLive(proofSetId)

// Get proof set details
const nextRootId = await pdpVerifier.getNextRootId(proofSetId)
const listener = await pdpVerifier.getProofSetListener(proofSetId)
const leafCount = await pdpVerifier.getProofSetLeafCount(proofSetId)

// Extract proof set ID from transaction receipt
const proofSetId = await pdpVerifier.extractProofSetIdFromReceipt(receipt)

PDP Server

Consolidated interface for all PDP server (Curio) HTTP operations including proof sets, uploads, and downloads.

import { PDPServer, PDPAuthHelper } from '@filoz/synapse-sdk/pdp'

// Create server instance with auth helper
const authHelper = new PDPAuthHelper(pandoraAddress, signer, chainId)
const pdpServer = new PDPServer(authHelper, 'https://pdp.provider.com', 'https://pdp.provider.com')

// Create a proof set
const { txHash, statusUrl } = await pdpServer.createProofSet(
  clientDataSetId,     // number
  payee,               // string (storage provider address)
  withCDN,             // boolean
  recordKeeper         // string (Pandora contract address)
)

// Check creation status
const status = await pdpServer.getProofSetCreationStatus(txHash)
console.log(`Status: ${status.txStatus}, Proof Set ID: ${status.proofSetId}`)

// Add roots to proof set
await pdpServer.addRoots(
  proofSetId,         // number (PDPVerifier proof set ID)
  clientDataSetId,    // number
  nextRootId,         // number (must match chain state)
  rootDataArray       // Array of { cid: string | CommP, rawSize: number }
)

// Upload a piece
const { commP, size } = await pdpServer.uploadPiece(data, 'my-file.dat')

// Find existing piece
const piece = await pdpServer.findPiece(commP, size)
console.log(`Piece found: ${piece.uuid}`)

// Download a piece
const data = await pdpServer.downloadPiece(commP)

// Get proof set details
const proofSet = await pdpServer.getProofSet(proofSetId)

PDP Auth Helper

Sign EIP-712 typed data for PDP operations. Compatible with MetaMask and other browser wallets.

import { PDPAuthHelper } from '@filoz/synapse-sdk/pdp'

// Create auth helper directly
const authHelper = new PDPAuthHelper(pandoraAddress, signer, chainId)

// Sign operations
const createProofSetSig = await authHelper.signCreateProofSet(
  clientDataSetId,    // number
  payeeAddress,       // string
  withCDN             // boolean
)

const addRootsSig = await authHelper.signAddRoots(
  clientDataSetId,    // number
  firstRootId,        // number
  rootDataArray       // Array of { cid: string | CommP, rawSize: number }
)

// All signatures return { signature, v, r, s, signedData }

CommP Utilities

Calculate and validate Filecoin Piece Commitments without instantiating the full SDK.

import { calculate, asCommP, createCommPStream } from '@filoz/synapse-sdk/commp'

// Calculate CommP from data
const data = new Uint8Array([1, 2, 3, 4])
const commp = calculate(data)
console.log(commp.toString()) // baga6ea4seaq...

// Validate and convert CommP strings and CIDs
const commp = asCommP('baga6ea4seaqao7s73y24kcutaosvacpdjgfe5pw76ooefnyqw4ynr3d2y6x2mpq')
if (commp !== null) {
  console.log('Valid CommP:', commp.toString())
}

// Stream-based CommP calculation; compatible with Web Streams API
const { stream, getCommP } = createCommPStream()
// Pipe data through stream, then call getCommP() for result

Network Configuration

RPC Endpoints

import { RPC_URLS } from '@filoz/synapse-sdk'

// Mainnet
RPC_URLS.mainnet.websocket  // wss://wss.node.glif.io/apigw/lotus/rpc/v1
RPC_URLS.mainnet.http       // https://api.node.glif.io/rpc/v1

// Calibration Testnet
RPC_URLS.calibration.websocket  // wss://wss.calibration.node.glif.io/apigw/lotus/rpc/v1
RPC_URLS.calibration.http       // https://api.calibration.node.glif.io/rpc/v1

GLIF Authorization

For higher rate limits with GLIF endpoints:

import { Synapse } from '@filoz/synapse-sdk'

// Using GLIF authorization with private key
const synapse = await Synapse.create({
  privateKey: '0x...',
  rpcURL: 'https://api.node.glif.io/rpc/v1',
  authorization: 'Bearer YOUR_GLIF_TOKEN'
})

Network Details

Filecoin Mainnet

  • Chain ID: 314
  • USDFC Contract: 0x80B98d3aa09ffff255c3ba4A241111Ff1262F045

Filecoin Calibration Testnet

  • Chain ID: 314159
  • USDFC Contract: 0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0

Browser Integration

The SDK works seamlessly in browsers.

MetaMask Setup

Official documentation for configuring MetaMask with Filecoin (both Mainnet and the Calibration Test network) can be found at: https://docs.filecoin.io/basics/assets/metamask-setup

If you want to add the Filecoin network programmatically, you can use the following code snippet, for Mainnet (change accordingly for Calibration Testnet):

// Add Filecoin network to MetaMask
await window.ethereum.request({
  method: 'wallet_addEthereumChain',
  params: [{
    chainId: '0x13A',  // 314 for mainnet
    chainName: 'Filecoin',
    nativeCurrency: { name: 'FIL', symbol: 'FIL', decimals: 18 },
    rpcUrls: ['https://api.node.glif.io/rpc/v1'],
    blockExplorerUrls: ['https://filfox.info/en']
  }]
})

Additional Information

Type Definitions

The SDK is fully typed with TypeScript. Key types include:

  • CommP - Filecoin Piece Commitment CID
  • TokenAmount - number | bigint for token amounts
  • StorageOptions - Options for storage service creation
  • AuthSignature - Signature data for authenticated operations

Error Handling

All SDK methods use descriptive error messages with proper error chaining:

try {
  await synapse.payments.deposit(amount)
} catch (error) {
  console.error(error.message)  // Clear error description
  console.error(error.cause)     // Underlying error if any
}

Contributing

Contributions are welcome! If using an AI tool, you are welcome to load AGENTS.md into your context to teach it about the structure and conventions of this SDK.

Testing

Run the test suite:

npm test              # Run all tests and linting
npm run test:node     # Node.js tests only
npm run test:browser  # Browser tests only

Migration Guide

Transaction Return Types (v0.7.0+)

Starting with version 0.7.0, payment methods now return ethers.TransactionResponse objects instead of transaction hashes. This provides more control and aligns with standard ethers.js patterns.

Before (v0.6.x and earlier):

// Methods returned transaction hash strings
const txHash = await synapse.payments.approve(token, spender, amount)
console.log(`Transaction: ${txHash}`)
// Transaction was already confirmed

After (v0.7.0+):

// Methods return TransactionResponse objects
const tx = await synapse.payments.approve(token, spender, amount)
console.log(`Transaction: ${tx.hash}`)
// Optional: wait for confirmation when you need it
const receipt = await tx.wait()
console.log(`Confirmed in block ${receipt.blockNumber}`)

Affected methods:

  • approve() - Returns TransactionResponse
  • approveService() - Returns TransactionResponse
  • revokeService() - Returns TransactionResponse
  • withdraw() - Returns TransactionResponse
  • deposit() - Returns TransactionResponse, plus new callbacks for multi-step visibility

Deposit callbacks (new):

const tx = await synapse.payments.deposit(amount, TOKENS.USDFC, {
  onAllowanceCheck: (current, required) => {
    console.log(`Checking allowance: ${current} vs ${required}`)
  },
  onApprovalTransaction: (approveTx) => {
    console.log(`Auto-approval sent: ${approveTx.hash}`)
  },
  onApprovalConfirmed: (receipt) => {
    console.log(`Approval confirmed in block ${receipt.blockNumber}`)
  },
  onDepositStarting: () => {
    console.log('Starting deposit transaction...')
  }
})

License

Apache-2.0

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 6