Skip to content

Migration Guide

If you are coming from an earlier version of any of the Synapse packages, you will need to make sure to update the APIs listed below.


This release changes upload cost inputs, enables automatic piece batching, and removes the piece metadata getters deprecated in 1.2.1. If you use core actions directly, also follow synapse-core 0.9.0.

Action: Use new data sets for compact storage

Section titled “Action: Use new data sets for compact storage”

PDPVerifier 3.5.0 introduces compact piece storage for data sets created after the network’s migration. Existing data sets keep their legacy format permanently, including newly appended pieces; neither the contract upgrade nor the SDK migrates them automatically.

Read Larger, Cheaper Storage Batches for gas savings, batching, and how to explicitly direct future uploads to new data sets. Reuse the new data sets after switching; changing application metadata is optional.

Action: Pass exact piece sizes to upload cost APIs

Section titled “Action: Pass exact piece sizes to upload cost APIs”

Replace dataSize with pieceSizes in storage.prepare(), storage.calculateMultiContextCosts(), and storage.getUploadCosts(). Remove pieceCount from getUploadCosts() calls: the number of pieces is now the array length.

// before: one piece
await synapse.storage.prepare({ dataSize })
// after: one piece
await synapse.storage.prepare({ pieceSizes: [dataSize] })
// after: multiple pieces, with the same pieces uploaded to every context
const pieceSizes = files.map((file) => BigInt(file.size))
await synapse.storage.prepare({ context: contexts, pieceSizes })

Supply each piece’s exact raw payload size as a bigint. Do not replace a multi-piece upload with [totalSize]: each piece is rounded independently to PDP leaves and incurs operation fees. Empty arrays and non-positive sizes are rejected.

When passing precomputed costs to prepare(), calculate them for the exact contexts and piece sizes. Use calculateMultiContextCosts(contexts, { pieceSizes }) for multiple copies; getUploadCosts() estimates only one context.

Action: Supply existing data-set state when estimating costs

Section titled “Action: Supply existing data-set state when estimating costs”

For storage.getUploadCosts({ isNewDataSet: false, ... }), replace currentDataSetSize with dataSetLeafCount and supply currentLifecycleReserveBalance and pdpEndEpoch. All three fields are required. Include pendingOneTimePayments when fees are pending; it defaults to 0n.

import { getDataSetLeafCount } from '@filoz/synapse-core/pdp-verifier'
import { getDataSet } from '@filoz/synapse-core/warm-storage'
const [dataSet, dataSetLeafCount] = await Promise.all([
getDataSet(synapse.readClient, { dataSetId }),
getDataSetLeafCount(synapse.readClient, { dataSetId }),
])
if (dataSet == null) throw new Error('Data set not found')
const costs = await synapse.storage.getUploadCosts({
isNewDataSet: false,
pieceSizes,
dataSetLeafCount,
currentLifecycleReserveBalance: dataSet.lifecycleReserveBalance,
pendingOneTimePayments: dataSet.pendingOneTimePayments,
pdpEndEpoch: dataSet.pdpEndEpoch,
})

Missing required state throws ValidationError. A non-zero pdpEndEpoch throws ServiceAlreadyTerminatedError, since the data set can no longer accept uploads.

To have the SDK read this state for you, use storage.prepare({ context, pieceSizes }) or storage.calculateMultiContextCosts(contexts, { pieceSizes }). DataSetInfo now includes the required pendingOneTimePayments and lifecycleReserveBalance fields; update any typed fixtures or adapters that construct it.

Action: Use the reserve-aware funding result

Section titled “Action: Use the reserve-aware funding result”

Use costs.depositNeeded for the amount to fund. Operation fees are paid from the lifecycle reserve and are no longer added directly to the deposit. Funding includes the initial reserve for a new data set and any replenishment required as pending fees drain the reserve. Do not add costs.fees.total to depositNeeded.

The lockup breakdown now includes lockups.reserveReplenishment and lockups.rateDeltaPerEpoch. Include replenishment when displaying the components of lockups.total. Fee and reserve estimates conservatively treat each supplied piece as a separate add-pieces operation; actual batched fees can be lower. See Storage Costs for the funding model.

Action: Account for automatic upload batching

Section titled “Action: Account for automatic upload batching”

storage.upload() and StorageContext.upload() now batch compatible concurrent uploads by default. Uploads to the same provider and data set can share a transaction, so multiple upload callbacks may report the same transaction hash. Each upload still returns its own result; check complete on each result.

The default wait is { kind: 'delay', ms: 0 }: the batch is submitted after a zero-delay window once its uploads and pulls finish parking. Start uploads concurrently to let them join a batch. Sequentially awaiting each upload prevents them from sharing a batch.

To preserve the previous upload path, disable batching when creating the SDK:

import { Synapse } from '@filoz/synapse-sdk'
const synapse = Synapse.create({ account, source: 'my-app', pieceBatching: false })
// With an existing client:
const fromClient = new Synapse({ client, source: 'my-app', pieceBatching: false })

If you select limiter mode, start the uploads before flushing. A pending batch waits until the next piece would exceed the count or message-size limit, or until you call storage.flush():

const synapse = Synapse.create({
account,
source: 'my-app',
pieceBatching: { wait: { kind: 'limiter' } },
})
const resultsPromise = Promise.allSettled(
files.map((file) => synapse.storage.upload(file.stream()))
)
await synapse.storage.flush()
const results = await resultsPromise

flush() waits for accepted uploads and pulls to finish parking, then submits their pending batches. It does not establish that every upload succeeded or was confirmed. Inspect the settled results for rejections and check complete on fulfilled upload results. Awaiting an upload before flushing in limiter mode can leave it waiting indefinitely.

Batched secondary pulls sign their per-piece authorization separately from the eventual commit batch. Interactive wallets can therefore prompt again at commit time. Use pieceBatching: false or the split operations with the same presigned extraData for pull and commit when you need signature reuse.

Newly added piece metadata is no longer stored in FWSS contract state, including additions to existing data sets. Upload and commit options still accept metadata, which is validated, signed, and emitted in PieceAdded events.

WarmStorageService.getPieceMetadata() and getPieceMetadataByKey() were deprecated in 1.2.1 and are removed in 2.0.0. Read piece metadata from FWSS PieceAdded events or an indexer.

For React integrations using the updated core, each data set returned by useDataSets() now has a pieces: Piece[] array whose items have no metadata field. Update components that read piece.metadata to use event or indexer data. See the core metadata migration for the removed actions and replacement types.


This release changes cost calculations and batch validation, and removes the piece metadata APIs deprecated in 0.8.1.

Action: Update core cost inputs and funding calculations

Section titled “Action: Update core cost inputs and funding calculations”

The following APIs from @filoz/synapse-core/warm-storage now require exact raw piece sizes:

APIInput changes
getUploadCosts()Replace dataSize and pieceCount with pieceSizes; replace currentDataSetSize with dataSetLeafCount
calculateDepositNeeded()Replace dataSize and pieceCount with pieceSizes; replace currentDataSetSize with dataSetLeafCount
calculateAdditionalLockupRequired()Replace dataSize with pieceSizes and currentDataSetSize with dataSetLeafCount
calculateUploadFees()Replace pieceCount with pieceSizes

pieceSizes must be a non-empty array of positive bigint byte sizes. Supply every piece separately, rather than a combined size. For the pure helpers that require dataSetLeafCount, use 0n for new data sets.

For existing data sets, getUploadCosts() and calculateDepositNeeded() require dataSetLeafCount, currentLifecycleReserveBalance, and pdpEndEpoch. Pass pendingOneTimePayments when fees are pending. Missing state throws ValidationError; a non-zero pdpEndEpoch throws ServiceAlreadyTerminatedError. Read this state as shown in the SDK cost example, and include clientAddress when calling the core action:

import { getUploadCosts } from '@filoz/synapse-core/warm-storage'
const costs = await getUploadCosts(client, {
clientAddress,
isNewDataSet: true,
pieceSizes: [1_000_000n, 2_000_000n],
})

Deposit calculations now fund lifecycle-reserve creation and replenishment instead of adding operation fees directly to the deposit. calculateDepositNeeded() exposes lockup.reserveReplenishment; getUploadCosts() exposes lockups.reserveReplenishment and lockups.rateDeltaPerEpoch. Fee and replenishment estimates price each piece as its own operation, so actual batching can cost less.

For custom cost calculations, use calculateUploadCosts() from @filoz/synapse-core/utils with resolved context, price-list, and account state. It aggregates context costs and applies account debt, available funds, runway, and buffer once. calculateAdditionalLockupRequired() alone does not calculate reserve replenishment; use calculateLifecycleReserveFunding() from /warm-storage if composing the individual helpers.

Action: Rename and relocate runway and buffer helpers

Section titled “Action: Rename and relocate runway and buffer helpers”

The old helpers were removed from @filoz/synapse-core/warm-storage. Their replacements keep the same input fields and are exported from @filoz/synapse-core/utils:

Removed exportReplacement export
calculateRunwayAmountcalculateRunwayAmountFromState
calculateBufferAmountcalculateBufferAmountFromState
calculateRunwayAmount.ParamsTypeCalculateRunwayAmountFromStateOptions
calculateBufferAmount.ParamsTypeCalculateBufferAmountFromStateOptions
import {
calculateBufferAmountFromState,
calculateRunwayAmountFromState,
} from '@filoz/synapse-core/utils'

Action: Replace data-set byte reads with leaf counts

Section titled “Action: Replace data-set byte reads with leaf counts”

getDataSetSizes() was removed from @filoz/synapse-core/pdp-verifier. Use getDataSetLeafCounts(), which returns a Map<bigint, bigint> keyed by data-set ID instead of a byte-size array ordered like the input. Duplicate IDs are read once, and non-live data sets have a leaf count of 0n.

import { getDataSetLeafCounts } from '@filoz/synapse-core/pdp-verifier'
import { leafCountToRawSize } from '@filoz/synapse-core/utils'
const leafCounts = await getDataSetLeafCounts(client, { dataSetIds })
for (const [dataSetId, leafCount] of leafCounts) {
console.log(dataSetId, leafCountToRawSize(leafCount))
}

Pass leaf counts directly to the new cost inputs. For custom rate calculations, combine the existing data-set leaf count with pieceSizesToLeafCount(pieceSizes), then call leafCountToRawSize() once on that total before passing it to calculateEffectiveRate({ sizeInBytes, ... }). This preserves per-piece leaf rounding and the contract’s aggregate conversion order.

leafCountToRawSize() converts leaves using leafCount × 32 × 127 / 128, rounded down. getAccountTotalStorageSize().totalSizeBytes now sums these FWSS-priced approximate sizes per live data set, replacing the previous padded-byte calculation. Use exact piece payload sizes for application accounting; this pricing approximation is not an exact sum of uploaded bytes.

Action: Replace count-only batch validation

Section titled “Action: Replace count-only batch validation”

validateAddPiecesBatch(count) was removed from @filoz/synapse-core/sp. Use assertAddPiecesFit() with actual PieceCIDs and metadata:

import { assertAddPiecesFit } from '@filoz/synapse-core/sp'
// Existing data set
assertAddPiecesFit({ kind: 'addPieces', pieces })
// New data set: include its metadata and CDN setting in the size estimate
assertAddPiecesFit({
kind: 'createDataSetAndAddPieces',
pieces,
metadata: dataSetMetadata,
cdn: withCDN,
})

Each piece uses { pieceCid, metadata? }. Validation enforces both the temporary 40-piece cap and the encoded message-size budget (SIZE_CONSTANTS.MAX_ADD_PIECES_MESSAGE_SIZE). Metadata can make a batch exceed the byte budget even within the count cap. Automatic batching splits pending pieces into fitting batches; explicit commit() and core add-pieces calls require callers to split oversized batches themselves.

Update error handling: oversized batches now throw AddPiecesBatchTooLargeError instead of TooManyPiecesError. PieceCIDs outside Curio’s upload-size bounds throw InvalidUploadSizeError; empty batches still throw AtLeastOnePieceRequiredError. These checks also apply to SDK commit(), presignForCommit(), and pull().

Use addPiecesFits() for a boolean count/message-size check when constructing batches. Use assertAddPiecesFit() when you also need PieceCID upload-size validation.

The following APIs were deprecated in 0.8.1 and are removed in 0.9.0:

  • getAllPieceMetadata(), getAllPieceMetadataCall(), and parseAllPieceMetadata() from @filoz/synapse-core/warm-storage
  • getPiecesWithMetadata() from @filoz/synapse-core/pdp-verifier
  • PieceWithMetadata from @filoz/synapse-core/warm-storage

Replace getPiecesWithMetadata() with getPieces() using the same options and page handling, and replace the PieceWithMetadata type with Piece:

import { getPieces } from '@filoz/synapse-core/pdp-verifier'
import type { Piece } from '@filoz/synapse-core/warm-storage'
const page = await getPieces(client, { dataSet, address, cursor })
const pieces: Piece[] = page.items

FWSS no longer persists piece metadata in contract state. Read it from FWSS PieceAdded events or an indexer. Upload and commit inputs continue to accept piece metadata for those events. Data-set metadata getters remain available.


Action: Migrate paginated reads to cursors and pages

Section titled “Action: Migrate paginated reads to cursors and pages”

Paginated actions in @filoz/synapse-core now share a bounded cursor interface:

type PaginationOptions = {
cursor?: bigint
limit?: bigint
}
type Page<T> = {
items: T[]
nextCursor?: bigint
}

Replace contract-specific offset, hasMore, and array result handling with cursor, items, and nextCursor. Treat nextCursor as opaque and pass it back unchanged. Omitting limit uses a bounded default; limit: 0n is now rejected instead of meaning “fetch everything.”

// before
const dataSets = await getClientDataSets(client, {
address,
offset: 0n,
limit: 0n,
})
// after: read one page
const page = await getClientDataSets(client, {
address,
limit: 100n,
})
console.log(page.items)
const nextPage = page.nextCursor === undefined
? undefined
: await getClientDataSets(client, {
address,
cursor: page.nextCursor,
limit: 100n,
})

Use the generic paginate() generator to traverse every page or accumulate all items:

import { paginate } from '@filoz/synapse-core'
import { getClientDataSets } from '@filoz/synapse-core/warm-storage'
for await (const dataSet of paginate(({ cursor }) =>
getClientDataSets(client, { address, cursor })
)) {
console.log(dataSet.dataSetId)
}
const allDataSets = await Array.fromAsync(
paginate(({ cursor }) => getClientDataSets(client, { address, cursor }))
)

This result change applies to paginated payment rails, FWSS client data sets and approved providers, PDP pieces and CID matches, and service-provider registry queries. Payment rail pages additionally include total.

The WarmStorageService.getClientDataSets() and getClientDataSetIds() methods in @filoz/synapse-sdk expose the same page interface. Higher-level SDK methods whose names promise all results, such as provider listing and rail listing methods, continue to return complete arrays and paginate internally.

Action: Replace getActivePieces with getActivePiecesByCursor

Section titled “Action: Replace getActivePieces with getActivePiecesByCursor”

The offset-based getActivePieces action was removed. Use piece-ID cursor pagination instead:

// before
const result = await getActivePieces(client, {
dataSetId,
offset: 0n,
limit: 100n,
})
// after
const page = await getActivePiecesByCursor(client, {
dataSetId,
limit: 100n,
})
// iterate
for await (const piece of paginate(({ cursor }) =>
getActivePiecesByCursor(client, { dataSetId, cursor, limit: 100n })
)) {
console.log(piece.id, piece.cid)
}

findPieceIdsByCid and getPieces also return pages and accept cursor rather than startPieceId or offset at the action level.

Raw *Call helpers in @filoz/synapse-core remain ABI-oriented: provide their required contract-facing offset or startPieceId and limit fields explicitly when constructing multicalls.

Action: Replace core activePieceCount with hasActivePieces

Section titled “Action: Replace core activePieceCount with hasActivePieces”

The enriched PdpDataSet values returned by getPdpDataSet() and getPdpDataSets() no longer include an exact activePieceCount. They now expose hasActivePieces, derived from a non-zero getDataSetLeafCount read. Leaf count is an O(1) storage lookup, unlike getActivePiecesByCursor and getActivePieceCount, which scan piece IDs and can run out of gas on large or fully drained data sets:

// before
const dataSet = await getPdpDataSet(client, { dataSetId })
if (dataSet && dataSet.activePieceCount > 0n) {
// the data set has pieces
}
// after
const dataSet = await getPdpDataSet(client, { dataSetId })
if (dataSet?.hasActivePieces) {
// the data set has pieces
}

WarmStorageService.hasActivePieces() keeps the same public API, but now uses the same leaf-count proxy instead of calculating an exact count. EnhancedDataSetInfo from getClientDataSetsWithDetails() / findDataSets() also exposes hasActivePieces instead of activePieceCount.

The core getActivePieceCount() action remains available, but the underlying contract getter scans the data set’s piece-ID range and can fail for large data sets. WarmStorageService.getActivePieceCount() now paginates getActivePiecesByCursor to derive an exact count:

const activePieceCount = await warmStorageService.getActivePieceCount({ dataSetId })

To paginate explicitly in core:

let activePieceCount = 0n
for await (const _piece of paginate(({ cursor }) =>
getActivePiecesByCursor(client, { dataSetId, cursor })
)) {
activePieceCount++
}

Action: Replace terminateDataSet with terminateService

Section titled “Action: Replace terminateDataSet with terminateService”

Data set termination is now service termination. terminateDataSet was removed.

// before
const hash = await synapse.storage.terminateDataSet({ dataSetId })
await synapse.client.waitForTransactionReceipt({ hash })
// after: provider-relayed by default
const result = await synapse.storage.terminateService({ dataSetId })
console.log(result.endEpoch)
// independent on-chain fallback
const direct = await synapse.storage.terminateService({ dataSetId, onChain: true })
console.log(direct.txHash, direct.endEpoch)

context.terminate() now uses the same provider-relayed default and returns { txHash?, dataSetId, endEpoch }.

getServicePrice() was removed from both @filoz/synapse-core and WarmStorageService. Use getPriceList(), which returns the full on-chain price catalogue (token, rates, fees, lockups).

// before
const price = await warmStorage.getServicePrice()
price.pricePerTiBPerMonthNoCDN
price.pricePerTiBCdnEgress
// after
const priceList = await warmStorage.getPriceList()
priceList.rates.storagePerTibPerMonth
priceList.rates.cdnEgressPerTib

The React useServicePrice() hook was removed in favor of usePriceList().

// before
import { useServicePrice } from '@filoz/synapse-react'
const { data } = useServicePrice()
data?.pricePerTiBPerMonthNoCDN
// after
import { usePriceList } from '@filoz/synapse-react'
const { data } = usePriceList()
data?.rates.storagePerTibPerMonth

Action: Read upload rates from costs.rates

Section titled “Action: Read upload rates from costs.rates”

The rate alias on upload-cost results was removed. Use rates.

// before
const { costs } = await synapse.storage.prepare({ dataSize })
costs.rate.perMonth
// after
const { costs } = await synapse.storage.prepare({ dataSize })
costs.rates.perMonth

Action: Replace the LOCKUP_PERIOD constant

Section titled “Action: Replace the LOCKUP_PERIOD constant”

The LOCKUP_PERIOD export was removed from @filoz/synapse-core. The lockup period is now read from the chain; use getPriceList().lockups.defaultLockupPeriod if you need the value.

Action: Re-mint session keys for service termination

Section titled “Action: Re-mint session keys for service termination”

DeleteDataSetPermission has been replaced by TerminateServicePermission.

Existing session keys granted with DeleteDataSetPermission will fail the Synapse.create() permission check. Re-authorize session keys so they include TerminateServicePermission.

// before
SessionKey.DeleteDataSetPermission
TypedData.signDeleteDataSet(client, { dataSetId })
// after
SessionKey.TerminateServicePermission
TypedData.signTerminateService(client, { dataSetId })

synapse-sdk moved to a viem-first API, removed deprecated modules/methods, and standardized method signatures around options objects plus bigint identifiers.

Action: Migrate from ethers setup to viem setup

Section titled “Action: Migrate from ethers setup to viem setup”
// before
import { Synapse } from '@filoz/synapse-sdk'
const synapse = await Synapse.create({
privateKey: PRIVATE_KEY,
rpcURL: 'https://api.calibration.node.glif.io/rpc/v1'
})
// after
import { calibration } from '@filoz/synapse-sdk'
import { privateKeyToAccount } from 'viem/accounts'
import { http } from 'viem'
import { Synapse } from '@filoz/synapse-sdk'
const synapse = Synapse.create({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
source: 'my-app',
chain: calibration, // optional
transport: http() // optional
})

Action: Remove deprecated SDK module imports

Section titled “Action: Remove deprecated SDK module imports”

The following subpath exports were removed from @filoz/synapse-sdk:

  • @filoz/synapse-sdk/pdp
  • @filoz/synapse-sdk/subgraph
  • @filoz/synapse-sdk/telemetry
// before
import { PDPAuthHelper, PDPServer, PDPVerifier } from '@filoz/synapse-sdk/pdp'
import { SubgraphService } from '@filoz/synapse-sdk/subgraph'
import { getGlobalTelemetry } from '@filoz/synapse-sdk/telemetry'
// after
import { Synapse } from '@filoz/synapse-sdk'
import { PaymentsService } from '@filoz/synapse-sdk/payments'
import { SPRegistryService } from '@filoz/synapse-sdk/sp-registry'
import { StorageContext, StorageManager } from '@filoz/synapse-sdk/storage'
import { WarmStorageService } from '@filoz/synapse-sdk/warm-storage'

Action: Convert positional parameters to object parameters

Section titled “Action: Convert positional parameters to object parameters”

Most service methods now take an options object. IDs are now bigint in the public API.

// before
await synapse.storage.download(pieceCid, { withCDN: true })
await synapse.payments.allowance(spender)
await synapse.payments.settle(12, 5000)
await synapse.providers.getProvider(1)
// after
await synapse.storage.download({ pieceCid, withCDN: true })
await synapse.payments.allowance({ spender })
await synapse.payments.settle({ railId: 12n, untilEpoch: 5000n })
await synapse.providers.getProvider({ providerId: 1n })

This change applies broadly across:

  • PaymentsService
  • WarmStorageService
  • SPRegistryService
  • retrievers and storage download APIs

Action: Replace removed deprecated methods

Section titled “Action: Replace removed deprecated methods”

Deprecated methods that were previously shimmed were removed from Synapse.

// before
const storage = await synapse.createStorage({ providerId: 1 })
const data = await synapse.download(pieceCid)
const info = await synapse.getStorageInfo()
// after
const context = await synapse.storage.createContext({ providerId: 1n })
const data = await synapse.storage.download({ pieceCid })
const info = await synapse.storage.getStorageInfo()

Action: Update dataset and callback assumptions

Section titled “Action: Update dataset and callback assumptions”
// before
if (dataSet.currentPieceCount > 0) {
// ...
}
callbacks: {
onPieceAdded: () => {},
onPieceConfirmed: () => {}
}
// after
if (dataSet.activePieceCount > 0n) {
// ...
}
callbacks: {
onPiecesAdded: (txHash, pieces) => {},
onPiecesConfirmed: (dataSetId, pieces) => {}
}
  1. Replace ethers-based initialization (privateKey/provider/signer) with viem (account + transport + chain).
  2. Remove imports from @filoz/synapse-sdk/pdp, @filoz/synapse-sdk/subgraph, and @filoz/synapse-sdk/telemetry.
  3. Migrate method calls to options-object style and switch numeric IDs to bigint.
  4. Replace removed deprecated methods (synapse.createStorage, synapse.download, synapse.getStorageInfo) with synapse.storage.*.
  5. Update dataset field usage (currentPieceCount -> activePieceCount) and callback names (onPieceAdded/onPieceConfirmed -> plural callbacks).

Starting with version 0.24.0, the SDK introduces comprehensive terminology changes to better align with Filecoin ecosystem conventions:

  • PandoraWarm Storage
  • Proof SetsData Sets
  • RootsPieces
  • Storage ProvidersService Providers
    • Note: most service providers are, in fact, storage providers, however this language reflects the emergence of new service types on Filecoin beyond storage.

This is a breaking change that affects imports, type names, method names, and configuration options throughout the SDK.

Before (v0.23.x and earlier):

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

After (v0.24.0+):

import { WarmStorageService } from '@filoz/synapse-sdk/warm-storage'
Old Type (< v0.24.0)New Type (v0.24.0+)
ProofSetIdDataSetId
RootDataPieceData
ProofSetInfoDataSetInfo
EnhancedProofSetInfoEnhancedDataSetInfo
ProofSetCreationStatusResponseDataSetCreationStatusResponse
RootAdditionStatusResponsePieceAdditionStatusResponse
StorageProviderServiceProvider

Synapse Class:

// Before (< v0.24.0)
synapse.getPandoraAddress()
// After (v0.24.0+)
synapse.getWarmStorageAddress()

WarmStorageService (formerly PandoraService):

// Before (< v0.24.0)
pandoraService.getClientProofSets(client)
pandoraService.getAddRootsInfo(proofSetId)
// After (v0.24.0+)
warmStorageService.getClientDataSets(client)
warmStorageService.getAddPiecesInfo(dataSetId)

PDPAuthHelper:

// Before (< v0.24.0)
authHelper.signCreateProofSet(serviceProvider, clientDataSetId)
authHelper.signAddRoots(proofSetId, rootData)
// After (v0.24.0+)
authHelper.signCreateDataSet(serviceProvider, clientDataSetId)
authHelper.signAddPieces(dataSetId, pieceData)

PDPServer:

// Before (< v0.24.0)
pdpServer.createProofSet(serviceProvider, clientDataSetId)
pdpServer.addRoots(proofSetId, clientDataSetId, nextRootId, rootData)
// After (v0.24.0+)
pdpServer.createDataSet(clientDataSetId, serviceProvider, metadata, recordKeeper)
pdpServer.addPieces(dataSetId, clientDataSetId, pieceData, metadata)

v0.24.0 introduces the SPRegistryService for on-chain provider management:

import { SPRegistryService } from '@filoz/synapse-sdk/sp-registry'
// Query and manage providers through the registry
const spRegistry = new SPRegistryService(provider, registryAddress)
const providers = await spRegistry.getAllActiveProviders()

This replaces previous provider discovery methods and provides a standardized way to register and manage service providers on-chain.

StorageService Properties:

// Before (< v0.24.0)
storage.storageProvider // Provider address property
// After (v0.24.0+)
storage.serviceProvider // Renamed property

Callback Interfaces:

// Before (< v0.24.0)
onProofSetResolved?: (info: { proofSetId: number }) => void
// After (v0.24.0+)
onDataSetResolved?: (info: { dataSetId: number }) => void

Before (< v0.24.0):

const synapse = await Synapse.create({
pandoraAddress: '0x...',
// ...
})

After (v0.24.0+):

const synapse = await Synapse.create({
warmStorageAddress: '0x...',
// ...
})

Before (< v0.24.0):

import { PandoraService } from '@filoz/synapse-sdk/pandora'
import type { StorageProvider } from '@filoz/synapse-sdk'
const pandoraService = new PandoraService(provider, pandoraAddress)
const proofSets = await pandoraService.getClientProofSets(client)
for (const proofSet of proofSets) {
console.log(`Proof set ${proofSet.railId} has ${proofSet.rootMetadata.length} roots`)
}
// Using storage service
const storage = await synapse.createStorage({
callbacks: {
onProofSetResolved: (info) => {
console.log(`Using proof set ${info.proofSetId}`)
}
}
})
console.log(`Storage provider: ${storage.storageProvider}`)

After (v0.24.0+):

import { WarmStorageService } from '@filoz/synapse-sdk/warm-storage'
import type { ServiceProvider } from '@filoz/synapse-sdk'
const warmStorageService = await WarmStorageService.create(provider, warmStorageAddress)
const dataSets = await warmStorageService.getClientDataSets(client)
for (const dataSet of dataSets) {
console.log(`Data set ${dataSet.railId} has ${dataSet.pieceMetadata.length} pieces`)
}
// Using new storage context API
const context = await synapse.storage.createContext({
callbacks: {
onDataSetResolved: (info) => {
console.log(`Using data set ${info.dataSetId}`)
}
}
})
console.log(`Service provider: ${context.serviceProvider}`)
// Downloads now use clearer method names
const data = await context.download(pieceCid) // Download from this context's provider
const anyData = await synapse.storage.download(pieceCid) // Download from any provider

The storage API has been redesigned for simplicity and clarity:

Simplified Storage API:

// Before (< v0.24.0)
const storage = await synapse.createStorage()
await storage.upload(data)
await storage.providerDownload(pieceCid) // Confusing method name
await synapse.download(pieceCid) // Duplicate functionality
// After (v0.24.0+) - Recommended approach
await synapse.storage.upload(data) // Simple: auto-managed contexts
await synapse.storage.download(pieceCid) // Simple: download from anywhere
// Advanced usage (when you need explicit control)
const context = await synapse.storage.createContext({ providerAddress: '0x...' })
await context.upload(data) // Upload to specific provider
await context.download(pieceCid) // Download from specific provider

Key improvements:

  • Access all storage operations via synapse.storage
  • Automatic context management - no need to explicitly create contexts for basic usage
  • Clear separation between SP-agnostic downloads (synapse.storage.download()) and context-specific downloads (context.download())

When upgrading from versions prior to v0.24.0:

  1. Update imports - Replace @filoz/synapse-sdk/pandora with @filoz/synapse-sdk/warm-storage
  2. Update type references:
    • Replace all ProofSet/proofSet with DataSet/dataSet
    • Replace all Root/root with Piece/piece
    • Replace StorageProvider type with ServiceProvider
  3. Update interface properties:
    • ApprovedProviderInfo.ownerApprovedProviderInfo.serviceProvider
    • ApprovedProviderInfo.pdpUrlApprovedProviderInfo.serviceURL
    • storage.storageProviderstorage.serviceProvider
  4. Update callback names:
    • onProofSetResolvedonDataSetResolved
    • Callback parameter proofSetIddataSetId
  5. Simplify storage API calls:
    • synapse.createStorage()synapse.storage.upload() (for simple usage)
    • synapse.createStorage()synapse.storage.createContext() (for advanced usage)
    • storage.providerDownload()context.download()
    • synapse.download()synapse.storage.download()
  6. Update method calls - Use the new method names as shown above
  7. Update configuration - Replace pandoraAddress with warmStorageAddress
  8. Update environment variables - PANDORA_ADDRESSWARM_STORAGE_ADDRESS
  9. Update GraphQL queries (if using subgraph) - proofSetsdataSets, rootspieces

All PaymentsService methods now consistently place the token parameter last with USDFC as the default:

Before (< v0.24.0):

await payments.allowance(TOKENS.USDFC, spender)
await payments.approve(TOKENS.USDFC, spender, amount)
await payments.deposit(amount, TOKENS.USDFC, callbacks)

After (v0.24.0+):

await payments.allowance(spender) // USDFC is default
await payments.approve(spender, amount) // USDFC is default
await payments.deposit(amount, TOKENS.USDFC, callbacks) // callbacks last for deposit

The SDK now automatically discovers all necessary contract addresses. The warmStorageAddress option in Synapse.create() has been removed as addresses are managed internally by the SDK for each network.

Note: There is no backward compatibility layer. All applications must update to the new terminology and API signatures when upgrading to v0.24.0 or later.