Storage Costs
Understanding storage costs helps you budget effectively and avoid service interruptions. This guide covers the SDK APIs for calculating costs, funding your account, and querying account state.
Storage operates on a pay-per-epoch model where you deposit USDFC tokens and set allowances that control how much the storage service can spend.
Pricing Components
Section titled “Pricing Components”| Component | Cost | Notes |
|---|---|---|
| Storage | $2.50/TiB/month | Minimum $0.06/month per data set (~24.567 GiB threshold) |
| Sybil Fee | 0.1 USDFC (one-time) | Per new data set creation; prevents state-growth spam |
| CDN Egress | $14/TiB downloaded | 1 USDFC top-up ≈ 71.5 GiB of downloads |
| CDN Setup | 1 USDFC (one-time) | Per data set; reusing existing data sets incurs no cost |
Pricing Logic:
- Storage < 24.567 GiB: Minimum $0.06/month applies
- Storage ≥ 24.567 GiB: Actual cost
(bytes / TiB) × $2.50/month - CDN data sets require 1 USDFC setup on first creation only
- CDN egress credits can be topped up anytime
Real-World Cost Examples
Section titled “Real-World Cost Examples”These examples use plain arithmetic for clarity. For programmatic cost calculation, use getUploadCosts() — see Querying Upload Costs below.
Example 1: NFT Collection (10,000 × 5 KiB ≈ 48.82 MiB)
Section titled “Example 1: NFT Collection (10,000 × 5 KiB ≈ 48.82 MiB)”// 48.82 MiB less than 24.567 GiB threshold// Price is $0.06/monthconst const PRICE_PER_MONTH: 0.06
PRICE_PER_MONTH = 0.06;const const months: 24
months = 24;const const PRICE_FOR_24_MONTHS: number
PRICE_FOR_24_MONTHS = const PRICE_PER_MONTH: 0.06
PRICE_PER_MONTH * 24; // 1.44 USDFC| Duration | Total Cost |
|---|---|
| 1 month | 0.06 USDFC |
| 24 months | 1.44 USDFC |
Example 2: User Content Platform with CDN
Section titled “Example 2: User Content Platform with CDN”- Storage: 1,000 users × 100 MiB ≈ 100,000 MiB
- Traffic: 1,000 users × 100 MiB/month ≈ 100,000 MiB/month egress
const const STORAGE_PRICE_PER_TIB_PER_MONTH: 2.5
STORAGE_PRICE_PER_TIB_PER_MONTH = 2.5; // $2.50/TiB/monthconst const CDN_EGRESS_PRICE_PER_TIB: 14
CDN_EGRESS_PRICE_PER_TIB = 14; // $14/TiB downloadedconst const storageMiB: 100000
storageMiB = 100_000;const const egressMiB: 100000
egressMiB = 100_000;
// Storage: 100,000 MiB ≈ 0.0953 TiBconst const storageTiB: number
storageTiB = const storageMiB: 100000
storageMiB / 1024 / 1024;
// Egress: 100,000 MiB ≈ 0.0953 TiBconst const egressTiB: number
egressTiB = const egressMiB: 100000
egressMiB / 1024 / 1024;
// Storage cost per month: 0.0953 TiB × $2.50 ≈ $0.238/monthconst const storageCostPerMonth: number
storageCostPerMonth = const storageTiB: number
storageTiB * const STORAGE_PRICE_PER_TIB_PER_MONTH: 2.5
STORAGE_PRICE_PER_TIB_PER_MONTH;
// Egress cost per month: 0.0953 TiB × $14 ≈ $1.334/monthconst const egressCostPerMonth: number
egressCostPerMonth = const egressTiB: number
egressTiB * const CDN_EGRESS_PRICE_PER_TIB: 14
CDN_EGRESS_PRICE_PER_TIB;
// Total cost per month: $0.238/month + $1.334/month ≈ $1.572/monthconst const totalCostPerMonth: number
totalCostPerMonth = const storageCostPerMonth: number
storageCostPerMonth + const egressCostPerMonth: number
egressCostPerMonth;
// Total cost for 24 months: $1.572/month × 24 ≈ $37.728const const totalCostFor24Months: number
totalCostFor24Months = const totalCostPerMonth: number
totalCostPerMonth * 24;| Cost Component | Per Month | 24 Months |
|---|---|---|
| Storage | ≈ 0.238 USDFC | ≈ 5.71 USDFC |
| CDN Egress | ≈ 1.334 USDFC | ≈ 32.016 USDFC |
| Total | ≈ 1.572 USDFC | ≈ 37.728 USDFC |
Querying Upload Costs
Section titled “Querying Upload Costs”Use getUploadCosts() to preview costs without executing any transaction. This is useful for displaying pricing in a UI or letting users confirm before proceeding.
Getting the costs for uploading to an existing dataset:
// With currentDataSetSize — accurate floor-aware deltaconst { const rate: { perEpoch: bigint; perMonth: bigint;}
rate, const depositNeeded: bigint
depositNeeded } = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.getUploadCosts(options: Omit<GetUploadCostsOptions, "clientAddress">): Promise<UploadCosts>
getUploadCosts({ isNewDataSet?: boolean | undefined
isNewDataSet: false, currentDataSetSize?: bigint | undefined
currentDataSetSize: 50n * const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type MiB: bigint
MiB, dataSize: bigint
dataSize: 100n * const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type MiB: bigint
MiB,})// Shows incremental cost (newRate - currentRate), handles floor-to-floor
// Without currentDataSetSize — safe overestimate for floor casesconst const costs2: getUploadCosts.OutputType
costs2 = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.getUploadCosts(options: Omit<GetUploadCostsOptions, "clientAddress">): Promise<UploadCosts>
getUploadCosts({ isNewDataSet?: boolean | undefined
isNewDataSet: false, dataSize: bigint
dataSize: 100n * const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type MiB: bigint
MiB,})// Accurate for above-floor datasets, overestimates for floor-priced onesGetting the costs for uploading to a new dataset:
const { const rate: { perEpoch: bigint; perMonth: bigint;}
rate, const depositNeeded: bigint
depositNeeded, const needsFwssMaxApproval: boolean
needsFwssMaxApproval, const ready: boolean
ready } = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.getUploadCosts(options: Omit<GetUploadCostsOptions, "clientAddress">): Promise<UploadCosts>
getUploadCosts({ dataSize: bigint
dataSize: 100n * const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type MiB: bigint
MiB, isNewDataSet?: boolean | undefined
isNewDataSet: true, withCDN?: boolean | undefined
withCDN: true,})
// Storage rate per epoch (30 seconds)var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Rate per epoch:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const rate: { perEpoch: bigint; perMonth: bigint;}
rate.perEpoch: bigint
perEpoch), "USDFC")// Storage rate per monthvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Rate per month:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const rate: { perEpoch: bigint; perMonth: bigint;}
rate.perMonth: bigint
perMonth), "USDFC")// USDFC to depositvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Deposit needed:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const depositNeeded: bigint
depositNeeded), "USDFC")// Whether FWSS needs to be approvedvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Needs approval:", const needsFwssMaxApproval: boolean
needsFwssMaxApproval)// Whether the account is ready to uploadvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Ready to upload:", const ready: boolean
ready)Upload Preparation
Section titled “Upload Preparation”Before uploading, the WarmStorage operator must be approved and your account must be funded. FWSS requires a 30-day prepayment buffer — when your balance drops below 30 days, the provider may remove your data.
prepare() computes costs and returns a ready-to-execute transaction. This is the recommended approach — it handles deposit calculation, operator approval, and contract call routing in one step.
const { const costs: getUploadCosts.OutputType
costs, const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;} | null
transaction } = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.prepare(options: PrepareOptions): Promise<PrepareResult>
prepare({ PrepareOptions.dataSize: bigint
dataSize: const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type GiB: bigint
GiB,})
// Inspect costsconst { const rate: { perEpoch: bigint; perMonth: bigint;}
rate, const depositNeeded: bigint
depositNeeded } = const costs: getUploadCosts.OutputType
costs // Upload costs breakdownvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Rate per month:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const rate: { perEpoch: bigint; perMonth: bigint;}
rate.perMonth: bigint
perMonth))var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Deposit needed:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const depositNeeded: bigint
depositNeeded))
// Execute if the account isn't readyif (const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;} | null
transaction) { const { const depositAmount: bigint
depositAmount, const includesApproval: boolean
includesApproval } = const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;}
transaction var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Deposit amount:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const depositAmount: bigint
depositAmount)) var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Includes approval:", const includesApproval: boolean
includesApproval)
const { const hash: `0x${string}`
hash } = await const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;}
transaction.execute: (options?: { onHash?: (hash: Hash) => void;}) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null;}>
execute() var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Transaction confirmed:", const hash: `0x${string}`
hash)}
// Now safe to uploadWhen storing data across multiple providers, pass an array of contexts. The SDK aggregates per-context lockup while computing account-level values (debt, runway, buffer) once.
import { class Synapse
Synapse, function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits, const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS, const TIME_CONSTANTS: { readonly EPOCH_DURATION: 30; readonly EPOCHS_PER_DAY: 2880n; readonly EPOCHS_PER_MONTH: 86400n; readonly DAYS_PER_MONTH: 30n; readonly DEFAULT_LOCKUP_DAYS: 30n; readonly PERMIT_DEADLINE_DURATION: 3600;}
TIME_CONSTANTS } from "@filoz/synapse-sdk"import { function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount } from 'viem/accounts'const const synapse: Synapse
synapse = class Synapse
Synapse.Synapse.create(options: SynapseOptions): Synapse
create({ SynapseOptions.account: `0x${string}` | Account
account: function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount('0x...'), SynapseOptions.source: string | null
source: 'my-app' })
const const contexts: StorageContext[]
contexts = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.createContexts(options?: CreateContextsOptions): Promise<StorageContext[]>
createContexts({ CreateContextsOptions.copies?: number | undefined
copies: 3 })
// 1-year runwayconst const oneYear: bigint
oneYear = const TIME_CONSTANTS: { readonly EPOCH_DURATION: 30; readonly EPOCHS_PER_DAY: 2880n; readonly EPOCHS_PER_MONTH: 86400n; readonly DAYS_PER_MONTH: 30n; readonly DEFAULT_LOCKUP_DAYS: 30n; readonly PERMIT_DEADLINE_DURATION: 3600;}
TIME_CONSTANTS.type EPOCHS_PER_MONTH: 86400n
EPOCHS_PER_MONTH * 12n
// Prepare a single deposit covering all 3 copies for 1 yearconst { const costs: getUploadCosts.OutputType
costs, const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;} | null
transaction } = await const synapse: Synapse
synapse.Synapse.storage: StorageManager
storage.StorageManager.prepare(options: PrepareOptions): Promise<PrepareResult>
prepare({ PrepareOptions.context?: StorageContext | StorageContext[] | undefined
context: const contexts: StorageContext[]
contexts, PrepareOptions.dataSize: bigint
dataSize: 50n * const SIZE_CONSTANTS: { readonly KiB: 1024n; readonly MiB: bigint; readonly GiB: bigint; readonly TiB: bigint; readonly PiB: bigint; readonly MAX_UPLOAD_SIZE: 1065353216; readonly MIN_UPLOAD_SIZE: 127; readonly DEFAULT_UPLOAD_BATCH_SIZE: 32; readonly BYTES_PER_LEAF: 32n;}
SIZE_CONSTANTS.type GiB: bigint
GiB, PrepareOptions.extraRunwayEpochs?: bigint | undefined
extraRunwayEpochs: const oneYear: bigint
oneYear,})
const { const rate: { perEpoch: bigint; perMonth: bigint;}
rate, const depositNeeded: bigint
depositNeeded, const ready: boolean
ready } = const costs: getUploadCosts.OutputType
costsvar console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Monthly rate (per copy):", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const rate: { perEpoch: bigint; perMonth: bigint;}
rate.perMonth: bigint
perMonth), "USDFC")var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Total deposit needed:", function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits(const depositNeeded: bigint
depositNeeded), "USDFC")var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Account ready:", const ready: boolean
ready)
if (const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;} | null
transaction) { const { const hash: `0x${string}`
hash } = await const transaction: { depositAmount: bigint; includesApproval: boolean; execute: (options?: { onHash?: (hash: Hash) => void; }) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null; }>;}
transaction.execute: (options?: { onHash?: (hash: Hash) => void;}) => Promise<{ hash: Hash; receipt: TransactionReceipt<bigint, number, "success" | "reverted", TransactionType> | null;}>
execute() var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Funded:", const hash: `0x${string}`
hash)}prepare() returns:
costs: The fullUploadCostsbreakdowntransaction: A transaction object withexecute(), ornullif the account is already ready
The transaction automatically picks the right contract call:
- Needs deposit + approval: calls
depositWithPermitAndApproveOperator - Needs approval only: calls
approveService - Needs deposit only: calls
depositWithPermit - Already ready: returns
transaction: null
Account Queries
Section titled “Account Queries”Read-only APIs for account-level cost visibility.
// Aggregate rate across all active railsconst { const ratePerEpoch: any
ratePerEpoch, const ratePerMonth: any
ratePerMonth } = await any
synapse.any
payments.any
totalAccountRate()
// Sum of lockupFixed across all rails (including terminated but not finalized)const { const totalFixedLockup: any
totalFixedLockup } = await any
synapse.any
payments.any
totalAccountLockup()
// Total storage size across all live datasetsimport { function getAccountTotalStorageSize(client: Client<Transport, Chain>, options: getAccountTotalStorageSize.OptionsType): Promise<getAccountTotalStorageSize.OutputType>
getAccountTotalStorageSize } from '@filoz/synapse-core/warm-storage'const { const totalSizeBytes: bigint
totalSizeBytes, const datasetCount: number
datasetCount } = await function getAccountTotalStorageSize(client: Client<Transport, Chain>, options: getAccountTotalStorageSize.OptionsType): Promise<getAccountTotalStorageSize.OutputType>
getAccountTotalStorageSize(any
client, { address: `0x${string}`
address: '0x...',})All rate values come in both per-epoch (contract-native) and per-month (ratePerEpoch * EPOCHS_PER_MONTH) units. Note that ratePerMonth from totalAccountRate() is computed as ratePerEpoch * EPOCHS_PER_MONTH — this is slightly less than the full-precision rate.perMonth returned by getUploadCosts() due to integer truncation. See Rate Precision for details.
Next Steps
Section titled “Next Steps”- Payments & Storage — Deep dive into the payment model mechanics
- StorageManager API Reference — Full SDK storage API
- PaymentsService API Reference — Full SDK payments API
- Storage Operations — Storage concepts and workflows
- Storage Context — Contexts and data sets