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: bigintwalletBalance = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.walletBalance(options?: {
token?: TokenIdentifier;
}): Promise<bigint>
walletBalance();
const const accountInfo: accounts.OutputTypeaccountInfo = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.accountInfo(options?: {
token?: TokenIdentifier;
}): Promise<accounts.OutputType>
accountInfo();
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Available:", const accountInfo: accounts.OutputTypeaccountInfo.availableFunds: bigintavailableFunds);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Locked:", const accountInfo: accounts.OutputTypeaccountInfo.lockupCurrent: bigintlockupCurrent);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Spending rate:", const accountInfo: accounts.OutputTypeaccountInfo.lockupRate: bigintlockupRate, "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: bigintamount = function parseUnits(value: string | number | bigint | Dnum, decimals?: number): bigintparseUnits("100");
const const hash: `0x${string}`hash = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.depositWithPermit(options: {
amount: TokenAmount;
token?: TokenIdentifier;
deadline?: bigint;
}): Promise<Hash>
depositWithPermit({ amount: bigintamount });
await const synapse: Synapsesynapse.Synapse.client: AccountClient<PublicRpcSchema, PublicActions<Transport, FilecoinChain>>client.waitForTransactionReceipt: (args: WaitForTransactionReceiptParameters<FilecoinChain>) => Promise<TransactionReceipt>Waits for the [Transaction](https://viem.sh/docs/glossary/terms#transaction) to be included on a [Block](https://viem.sh/docs/glossary/terms#block) (one confirmation), and then returns the [Transaction Receipt](https://viem.sh/docs/glossary/terms#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`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getTransactionReceipt) on each block until it has been processed.
- If a Transaction has been replaced:
- Calls [`eth_getBlockByNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) and extracts the transactions
- Checks if one of the Transactions is a replacement
- If so, calls [`eth_getTransactionReceipt`](https://ethereum.org/en/developers/docs/apis/json-rpc/#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:
const const filecoinpay: `0x${string}`filecoinpay = const synapse: Synapsesynapse.Synapse.chain: FilecoinChainchain.FilecoinChain.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 fwssView;
};
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 contractscontracts.filecoinPay: {
address: Address;
abi: typeof filecoinPayV1Abi;
}
filecoinPay.address: `0x${string}`address
const const amount: bigintamount = function parseUnits(value: string | number | bigint | Dnum, decimals?: number): bigintparseUnits("100");
// 1. Approve USDFC spending
await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.approve(options: {
spender: Address;
amount: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
approve({spender: `0x${string}`spender: const filecoinpay: `0x${string}`filecoinpay, amount: bigintamount});
// 2. Deposit
await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.deposit(options: DepositOptions): Promise<Hash>deposit({ DepositOptions.amount: bigintamount });This requires two transactions and higher gas costs. Use depositWithPermit instead.
Account Summary
Section titled “Account Summary”Get a comprehensive snapshot of your account’s payment state in a single call. This is the recommended way to check account health: it returns derived values like debt, lockup breakdown, and runway.
const const summary: getAccountSummary.OutputTypesummary = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.accountSummary(options?: {
token?: TokenIdentifier;
epoch?: bigint;
}): Promise<getAccountSummary.OutputType>
accountSummary();
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Funds:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.funds: bigintfunds));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Available:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.availableFunds: bigintavailableFunds));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Debt:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.debt: bigintdebt));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Rate per epoch:", const summary: getAccountSummary.OutputTypesummary.lockupRatePerEpoch: bigintlockupRatePerEpoch);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Rate per month:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.lockupRatePerMonth: bigintlockupRatePerMonth));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Total lockup:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.totalLockup: biginttotalLockup));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(" Fixed lockup:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.totalFixedLockup: biginttotalFixedLockup));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(" Rate-based lockup:", function formatUnits(value: bigint, options?: FormatUnitsOptions): stringformatUnits(const summary: getAccountSummary.OutputTypesummary.totalRateBasedLockup: biginttotalRateBasedLockup));
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Runway (epochs):", const summary: getAccountSummary.OutputTypesummary.runwayInEpochs: bigintrunwayInEpochs);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Gross coverage (epochs):", const summary: getAccountSummary.OutputTypesummary.grossCoverageInEpochs: bigintgrossCoverageInEpochs);Two runway numbers, differing by the lockup reserve:
runwayInEpochs: runway excluding the lockup reserve. How long until your account enters deficit and the standard payment flow halts. Use for “when must I act?”.grossCoverageInEpochs: runway including the lockup reserve (funds / lockupRate). The full horizon your deposit covers at the current rate. Use for “how much have I prepaid in total?”.
The lockup reserve (totalLockup) is the portion of funds each rail has set aside under its terms. The funds are locked at the contract level while the rail is active, they can’t be withdrawn. Active payments draw from the unreserved portion of funds; the reserve sits as a floor. Once you enter deficit, standard settlement halts. A provider can then terminate the rail to claim against the reserve for up to one lockup period. Termination is one-way: once a rail has an endEpoch it’s heading to finalization and topping up your account won’t revive it. Top up before deficit to keep existing rails open.
Account Health Monitoring
Section titled “Account Health Monitoring”Important: Monitor your account health regularly. Insufficient balance causes payment failures and service interruptions.
import { function epochsToDays(epochs: bigint): bigintepochsToDays } from "@filoz/synapse-core/utils";
const const summary: getAccountSummary.OutputTypesummary = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.accountSummary(options?: {
token?: TokenIdentifier;
epoch?: bigint;
}): Promise<getAccountSummary.OutputType>
accountSummary();
const const runwayDays: bigintrunwayDays = function epochsToDays(epochs: bigint): bigintepochsToDays(const summary: getAccountSummary.OutputTypesummary.runwayInEpochs: bigintrunwayInEpochs);
const const totalDays: biginttotalDays = function epochsToDays(epochs: bigint): bigintepochsToDays(const summary: getAccountSummary.OutputTypesummary.grossCoverageInEpochs: bigintgrossCoverageInEpochs);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Runway: ${const runwayDays: bigintrunwayDays} days`);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Gross coverage: ${const totalDays: biginttotalDays} days`);
if (const runwayDays: bigintrunwayDays === 0n && const summary: getAccountSummary.OutputTypesummary.funds: bigintfunds > 0n) {
var console: Consoleconsole.Console.warn(...data: any[]): voidThe **`console.warn()`** static method outputs a warning message to the console at the "warning" log level. The message is only displayed to the user if the console is configured to display warning output. In most cases, the log level is configured within the console UI. The message may receive special formatting, such as yellow colors and a warning icon.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static)warn("Account in deficit: standard settlement to providers has halted. Deposit to resume.");
} else if (const runwayDays: bigintrunwayDays < 7n) {
var console: Consoleconsole.Console.warn(...data: any[]): voidThe **`console.warn()`** static method outputs a warning message to the console at the "warning" log level. The message is only displayed to the user if the console is configured to display warning output. In most cases, the log level is configured within the console UI. The message may receive special formatting, such as yellow colors and a warning icon.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static)warn("Low runway, top up soon to avoid service interruption.");
}epochsToDays floors to whole days and passes maxUint256 through unchanged (use that as the “infinite” sentinel when the account has no spend rate).
Withdrawing Unlocked Funds
Section titled “Withdrawing Unlocked Funds”const const info: accounts.OutputTypeinfo = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.accountInfo(options?: {
token?: TokenIdentifier;
}): Promise<accounts.OutputType>
accountInfo();
await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.withdraw(options: {
amount: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
withdraw({ amount: bigintamount: const info: accounts.OutputTypeinfo.availableFunds: bigintavailableFunds });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_HOUR: 120n;
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";
const const months: 3nmonths = 3n;
const const rateAllowance: bigintrateAllowance = const monthlyPricePerTiB: bigintmonthlyPricePerTiB / const TIME_CONSTANTS: {
readonly EPOCH_DURATION: 30;
readonly EPOCHS_PER_HOUR: 120n;
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: 86400nEPOCHS_PER_MONTH;
const const lockupAllowance: bigintlockupAllowance = const monthlyPricePerTiB: bigintmonthlyPricePerTiB * const months: 3nmonths;
const const maxLockupPeriod: 86400nmaxLockupPeriod = const TIME_CONSTANTS: {
readonly EPOCH_DURATION: 30;
readonly EPOCHS_PER_HOUR: 120n;
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: 86400nEPOCHS_PER_MONTH;
await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.approveService(options?: {
service?: Address;
rateAllowance?: TokenAmount;
lockupAllowance?: TokenAmount;
maxLockupPeriod?: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
approveService();Revoking an Operator
Section titled “Revoking an Operator”Revoking warmStorage’s operator approvals.
await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.revokeService(options?: {
service?: Address;
token?: TokenIdentifier;
}): Promise<Hash>
revokeService();Checking Operator Approvals
Section titled “Checking Operator Approvals”const const approval: operatorApprovals.OutputTypeapproval = await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.PaymentsService.serviceApproval(options?: {
service?: Address;
token?: TokenIdentifier;
}): Promise<operatorApprovals.OutputType>
serviceApproval();
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Approved:", const approval: operatorApprovals.OutputTypeapproval.isApproved: booleanisApproved);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(
"Rate allowance:",
const approval: operatorApprovals.OutputTypeapproval.rateAllowance: bigintrateAllowance,
"used:",
const approval: operatorApprovals.OutputTypeapproval.rateUsage: bigintrateUsage
);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(
"Lockup allowance:",
const approval: operatorApprovals.OutputTypeapproval.lockupAllowance: bigintlockupAllowance,
"used:",
const approval: operatorApprovals.OutputTypeapproval.lockupUsage: bigintlockupUsage
);
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Max lockup period:", const approval: operatorApprovals.OutputTypeapproval.maxLockupPeriod: bigintmaxLockupPeriod);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 →