Payment Operations
The Synapse SDK uses USDFC (a Filecoin-native stablecoin) for storage payments. Before uploading files, you must fund your account and approve operators.
This guide covers the essential operations. For advanced topics (understanding rails, settlement strategies), see Rails & Settlement.
Before working with payments, familiarize yourself with these fundamental concepts:
Key Concepts
Section titled “Key Concepts”- USDFC Token: Stablecoin on Filecoin which is used for all storage payments. The protocol requires USDFC approval before operations.
- Payment Rails: Continuous payment streams created automatically when you upload files. Rate is fixed per epoch.
- Epochs: Time periods (one epoch is 30 seconds). Payments accumulate per epoch and can be settled periodically.
- Operator Allowances: Permissions that allow contracts (like WarmStorage) to spend your USDFC and create payment rails.
Account Management
Section titled “Account Management”Check Your Balance
Section titled “Check Your Balance”Monitor your account balance to ensure you have sufficient funds for storage operations.
const const walletBalance: bigint
walletBalance = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.walletBalance(token?: TokenIdentifier): Promise<bigint>
walletBalance();const const accountInfo: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
accountInfo = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.accountInfo(token?: TokenIdentifier): Promise<{ funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}>
accountInfo();
var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Available:", const accountInfo: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
accountInfo.availableFunds: bigint
availableFunds);var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Locked:", const accountInfo: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
accountInfo.lockupCurrent: bigint
lockupCurrent);var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Spending rate:", const accountInfo: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
accountInfo.lockupRate: bigint
lockupRate, "per epoch");Fund Your Account
Section titled “Fund Your Account”import { const TOKENS: { readonly USDFC: "USDFC"; readonly FIL: "FIL";}
TOKENS } from "@filoz/synapse-sdk";
const const amount: bigint
amount = function parseUnits(value: string | number | bigint | Dnum, decimals?: number): bigint
parseUnits("100");const const hash: `0x${string}`
hash = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.depositWithPermit(amount: TokenAmount, token?: TokenIdentifier, deadline?: bigint): Promise<Hash>
depositWithPermit(const amount: bigint
amount);await const synapse: Synapse
synapse.Synapse.client: Client<Transport, Chain, Account, PublicRpcSchema, PublicActions<Transport, Chain>>
client.waitForTransactionReceipt: (args: WaitForTransactionReceiptParameters<Chain>) => Promise<TransactionReceipt>
Waits for the Transaction to be included on a Block (one confirmation), and then returns the Transaction Receipt. If the Transaction reverts, then the action will throw an error.
- Docs: https://viem.sh/docs/actions/public/waitForTransactionReceipt
- Example: https://stackblitz.com/github/wevm/viem/tree/main/examples/transactions_sending-transactions
- JSON-RPC Methods:
- Polls
eth_getTransactionReceipt on each block until it has been processed.
- If a Transaction has been replaced:
- Calls
eth_getBlockByNumber and extracts the transactions
- Checks if one of the Transactions is a replacement
- If so, calls
eth_getTransactionReceipt.
waitForTransactionReceipt({ hash: `0x${string}`
The hash of the transaction.
hash });Alternative: Two-Transaction Deposit (not recommended)
If you cannot use permit-based deposits, you can use the traditional two-transaction approach:
// 1. Approve USDFC spendingawait usdfc.approve(paymentAddress, depositAmount);
// 2. Depositawait synapse.payments.deposit(depositAmount);This requires two transactions and higher gas costs. Use depositWithPermit instead.
Account Health Monitoring
Section titled “Account Health Monitoring”Important: Monitor your account health regularly. Insufficient balance causes payment failures and service interruptions.
import { 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;}
TIME_CONSTANTS } from "@filoz/synapse-sdk";
const const info: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
info = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.accountInfo(token?: TokenIdentifier): Promise<{ funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}>
accountInfo();const const epochsRemaining: bigint
epochsRemaining = const info: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
info.availableFunds: bigint
availableFunds / const info: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
info.lockupRate: bigint
lockupRate;const const daysRemaining: number
daysRemaining = var Number: NumberConstructor(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(const epochsRemaining: bigint
epochsRemaining) / var Number: NumberConstructor(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(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;}
TIME_CONSTANTS.type EPOCHS_PER_DAY: 2880n
EPOCHS_PER_DAY);
var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log(`Days remaining: ${const daysRemaining: number
daysRemaining.Number.toFixed(fractionDigits?: number): string
Returns a string representing a number in fixed-point notation.
toFixed(1)}`);if (const daysRemaining: number
daysRemaining < 7) var console: Console
console.Console.warn(...data: any[]): void
The console.warn() static method outputs a warning message to the console at the 'warning' log level.
warn("⚠️ Low balance!");Withdrawing Unlocked Funds
Section titled “Withdrawing Unlocked Funds”const const info: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
info = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.accountInfo(token?: TokenIdentifier): Promise<{ funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}>
accountInfo();await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.withdraw(amount: TokenAmount, token?: TokenIdentifier): Promise<Hash>
withdraw(const info: { funds: bigint; lockupCurrent: bigint; lockupRate: bigint; lockupLastSettledAt: bigint; availableFunds: bigint;}
info.availableFunds: bigint
availableFunds);Approving Operators
Section titled “Approving Operators”Operators are smart contracts authorized to spend your tokens for specific services. Before uploading files, you must approve the WarmStorage operator. This approval is required only once per operator and grants permission to create payment rails on your behalf.
Three types of allowances protect your funds from unauthorized spending:
- Rate Allowance - Max spending per epoch across all rails
- Lockup Allowance - Max total funds locked across all rails
- Max Lockup Period - Max duration funds can be locked (the safety net)
Approving an Operator
Section titled “Approving an Operator”Approving warmStorage for 1TiB of storage for 3 months.
import { 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;}
TIME_CONSTANTS } from "@filoz/synapse-sdk";
const const months: 3n
months = 3n;
const const rateAllowance: bigint
rateAllowance = const monthlyPricePerTiB: bigint
monthlyPricePerTiB / 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;}
TIME_CONSTANTS.type EPOCHS_PER_MONTH: 86400n
EPOCHS_PER_MONTH;const const lockupAllowance: bigint
lockupAllowance = const monthlyPricePerTiB: bigint
monthlyPricePerTiB * const months: 3n
months;const const maxLockupPeriod: 86400n
maxLockupPeriod = 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;}
TIME_CONSTANTS.type EPOCHS_PER_MONTH: 86400n
EPOCHS_PER_MONTH;
await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.approveService(service: Address, rateAllowance: TokenAmount, lockupAllowance: TokenAmount, maxLockupPeriod: TokenAmount, token?: TokenIdentifier): Promise<Hash>
approveService( const calibration: Chain
calibration.Chain.contracts: { multicall3: ChainContract; usdfc: { address: Address; abi: typeof erc20WithPermit; }; filecoinPay: { address: Address; abi: typeof filecoinPayV1Abi; }; fwss: { address: Address; abi: typeof fwss; }; fwssView: { address: Address; abi: typeof filecoinWarmStorageServiceStateViewAbi; }; serviceProviderRegistry: { address: Address; abi: typeof serviceProviderRegistry; }; sessionKeyRegistry: { address: Address; abi: typeof sessionKeyRegistryAbi; }; pdp: { address: Address; abi: typeof pdpVerifierAbi; }; endorsements: { address: Address; abi: typeof providerIdSetAbi; };}
Collection of contracts
contracts.fwss: { address: Address; abi: typeof fwss;}
fwss.address: `0x${string}`
address, const rateAllowance: bigint
rateAllowance, const lockupAllowance: bigint
lockupAllowance, const maxLockupPeriod: 86400n
maxLockupPeriod);Revoking an Operator
Section titled “Revoking an Operator”Revoking warmStorage’s operator approvals.
await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.revokeService(service: Address, token?: TokenIdentifier): Promise<Hash>
revokeService(const calibration: Chain
calibration.Chain.contracts: { multicall3: ChainContract; usdfc: { address: Address; abi: typeof erc20WithPermit; }; filecoinPay: { address: Address; abi: typeof filecoinPayV1Abi; }; fwss: { address: Address; abi: typeof fwss; }; fwssView: { address: Address; abi: typeof filecoinWarmStorageServiceStateViewAbi; }; serviceProviderRegistry: { address: Address; abi: typeof serviceProviderRegistry; }; sessionKeyRegistry: { address: Address; abi: typeof sessionKeyRegistryAbi; }; pdp: { address: Address; abi: typeof pdpVerifierAbi; }; endorsements: { address: Address; abi: typeof providerIdSetAbi; };}
Collection of contracts
contracts.fwss: { address: Address; abi: typeof fwss;}
fwss.address: `0x${string}`
address);Checking Operator Approvals
Section titled “Checking Operator Approvals”const const approval: operatorApprovals.OutputType
approval = await const synapse: Synapse
synapse.Synapse.payments: PaymentsService
payments.PaymentsService.serviceApproval(service: Address, token?: TokenIdentifier): Promise<operatorApprovals.OutputType>
serviceApproval(const calibration: Chain
calibration.Chain.contracts: { multicall3: ChainContract; usdfc: { address: Address; abi: typeof erc20WithPermit; }; filecoinPay: { address: Address; abi: typeof filecoinPayV1Abi; }; fwss: { address: Address; abi: typeof fwss; }; fwssView: { address: Address; abi: typeof filecoinWarmStorageServiceStateViewAbi; }; serviceProviderRegistry: { address: Address; abi: typeof serviceProviderRegistry; }; sessionKeyRegistry: { address: Address; abi: typeof sessionKeyRegistryAbi; }; pdp: { address: Address; abi: typeof pdpVerifierAbi; }; endorsements: { address: Address; abi: typeof providerIdSetAbi; };}
Collection of contracts
contracts.fwss: { address: Address; abi: typeof fwss;}
fwss.address: `0x${string}`
address);
var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Approved:", const approval: operatorApprovals.OutputType
approval.isApproved: boolean
isApproved);var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log( "Rate allowance:", const approval: operatorApprovals.OutputType
approval.rateAllowance: bigint
rateAllowance, "used:", const approval: operatorApprovals.OutputType
approval.rateUsage: bigint
rateUsage);var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log( "Lockup allowance:", const approval: operatorApprovals.OutputType
approval.lockupAllowance: bigint
lockupAllowance, "used:", const approval: operatorApprovals.OutputType
approval.lockupUsage: bigint
lockupUsage);var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log("Max lockup period:", const approval: operatorApprovals.OutputType
approval.maxLockupPeriod: bigint
maxLockupPeriod);Next Steps
Section titled “Next Steps”- Understanding Rails & Settlement - Deep dive into payment mechanics
- Storage Costs & Budgeting - Plan your storage budget
- Ready to upload files? You now have the basics. Start uploading →