Skip to content

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:

  • 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.

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[]): void
The **`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[]): void
The **`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[]): void
The **`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");
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).
@remarksThe `waitForTransactionReceipt` action additionally supports Replacement detection (e.g. sped up Transactions). Transactions can be replaced when a user modifies their transaction in their wallet (to speed up or cancel). Transactions are replaced when they are sent from the same nonce. There are 3 types of Transaction Replacement reasons: - `repriced`: The gas price has been modified (e.g. different `maxFeePerGas`) - `cancelled`: The Transaction has been cancelled (e.g. `value === 0n`) - `replaced`: The Transaction has been replaced (e.g. different `value` or `data`)@paramargs - {@link WaitForTransactionReceiptParameters}@returnsThe transaction receipt. {@link WaitForTransactionReceiptReturnType}@exampleimport { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' const client = createPublicClient({ chain: mainnet, transport: http(), }) const transactionReceipt = await client.waitForTransactionReceipt({ hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d', })
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 contracts
contracts
.
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.

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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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.

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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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).

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 });

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:

  1. Rate Allowance - Max spending per epoch across all rails
  2. Lockup Allowance - Max total funds locked across all rails
  3. Max Lockup Period - Max duration funds can be locked (the safety net)

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 warmStorage’s operator approvals.

await const synapse: Synapsesynapse.Synapse.payments: PaymentsServicepayments.
PaymentsService.revokeService(options?: {
    service?: Address;
    token?: TokenIdentifier;
}): Promise<Hash>
revokeService
();
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[]): void
The **`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[]): void
The **`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[]): void
The **`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[]): void
The **`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);