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: bigint
walletBalance
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.walletBalance(options?: {
token?: TokenIdentifier;
}): Promise<bigint>
walletBalance
();
const
const accountInfo: accounts.OutputType
accountInfo
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.accountInfo(options?: {
token?: TokenIdentifier;
}): Promise<accounts.OutputType>
accountInfo
();
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Available:",
const accountInfo: accounts.OutputType
accountInfo
.
availableFunds: bigint
availableFunds
);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Locked:",
const accountInfo: accounts.OutputType
accountInfo
.
lockupCurrent: bigint
lockupCurrent
);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Spending rate:",
const accountInfo: accounts.OutputType
accountInfo
.
lockupRate: bigint
lockupRate
, "per epoch");
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(options: {
amount: TokenAmount;
token?: TokenIdentifier;
deadline?: bigint;
}): Promise<Hash>
depositWithPermit
({
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.

@remarks

The 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 - WaitForTransactionReceiptParameters

@returnsThe transaction receipt. WaitForTransactionReceiptReturnType

@example

import { 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: Synapse
synapse
.
Synapse.chain: Chain
chain
.
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
.
filecoinPay: {
address: Address;
abi: typeof filecoinPayV1Abi;
}
filecoinPay
.
address: `0x${string}`
address
const
const amount: bigint
amount
=
function parseUnits(value: string | number | bigint | Dnum, decimals?: number): bigint
parseUnits
("100");
// 1. Approve USDFC spending
await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.approve(options: {
spender: Address;
amount: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
approve
({
spender: `0x${string}`
spender
:
const filecoinpay: `0x${string}`
filecoinpay
,
amount: bigint
amount
});
// 2. Deposit
await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.deposit(options: DepositOptions): Promise<Hash>
deposit
({
DepositOptions.amount: bigint
amount
});

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.OutputType
summary
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.accountSummary(options?: {
token?: TokenIdentifier;
epoch?: bigint;
}): Promise<getAccountSummary.OutputType>
accountSummary
();
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Funds:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
funds: bigint
funds
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Available:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
availableFunds: bigint
availableFunds
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Debt:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
debt: bigint
debt
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Rate per epoch:",
const summary: getAccountSummary.OutputType
summary
.
lockupRatePerEpoch: bigint
lockupRatePerEpoch
);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Rate per month:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
lockupRatePerMonth: bigint
lockupRatePerMonth
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Total lockup:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
totalLockup: bigint
totalLockup
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(" Fixed lockup:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
totalFixedLockup: bigint
totalFixedLockup
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(" Rate-based lockup:",
function formatUnits(value: bigint, options?: FormatUnitsOptions): string
formatUnits
(
const summary: getAccountSummary.OutputType
summary
.
totalRateBasedLockup: bigint
totalRateBasedLockup
));
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Runway (epochs):",
const summary: getAccountSummary.OutputType
summary
.
runwayInEpochs: bigint
runwayInEpochs
);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
("Gross coverage (epochs):",
const summary: getAccountSummary.OutputType
summary
.
grossCoverageInEpochs: bigint
grossCoverageInEpochs
);

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): bigint
epochsToDays
} from "@filoz/synapse-core/utils";
const
const summary: getAccountSummary.OutputType
summary
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.accountSummary(options?: {
token?: TokenIdentifier;
epoch?: bigint;
}): Promise<getAccountSummary.OutputType>
accountSummary
();
const
const runwayDays: bigint
runwayDays
=
function epochsToDays(epochs: bigint): bigint
epochsToDays
(
const summary: getAccountSummary.OutputType
summary
.
runwayInEpochs: bigint
runwayInEpochs
);
const
const totalDays: bigint
totalDays
=
function epochsToDays(epochs: bigint): bigint
epochsToDays
(
const summary: getAccountSummary.OutputType
summary
.
grossCoverageInEpochs: bigint
grossCoverageInEpochs
);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(`Runway: ${
const runwayDays: bigint
runwayDays
} days`);
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(`Gross coverage: ${
const totalDays: bigint
totalDays
} days`);
if (
const runwayDays: bigint
runwayDays
=== 0n &&
const summary: getAccountSummary.OutputType
summary
.
funds: bigint
funds
> 0n) {
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. 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

warn
("Account in deficit: standard settlement to providers has halted. Deposit to resume.");
} else if (
const runwayDays: bigint
runwayDays
< 7n) {
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. 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

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.OutputType
info
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.accountInfo(options?: {
token?: TokenIdentifier;
}): Promise<accounts.OutputType>
accountInfo
();
await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.withdraw(options: {
amount: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
withdraw
({
amount: bigint
amount
:
const info: accounts.OutputType
info
.
availableFunds: bigint
availableFunds
});

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: 3n
months
= 3n;
const
const rateAllowance: bigint
rateAllowance
=
const monthlyPricePerTiB: bigint
monthlyPricePerTiB
/
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: 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_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: 86400n
EPOCHS_PER_MONTH
;
await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.approveService(options?: {
service?: Address;
rateAllowance?: TokenAmount;
lockupAllowance?: TokenAmount;
maxLockupPeriod?: TokenAmount;
token?: TokenIdentifier;
}): Promise<Hash>
approveService
();

Revoking warmStorage’s operator approvals.

await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.revokeService(options?: {
service?: Address;
token?: TokenIdentifier;
}): Promise<Hash>
revokeService
();
const
const approval: operatorApprovals.OutputType
approval
= await
const synapse: Synapse
synapse
.
Synapse.payments: PaymentsService
payments
.
PaymentsService.serviceApproval(options?: {
service?: Address;
token?: TokenIdentifier;
}): Promise<operatorApprovals.OutputType>
serviceApproval
();
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

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.

MDN Reference

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.

MDN Reference

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.

MDN Reference

log
("Max lockup period:",
const approval: operatorApprovals.OutputType
approval
.
maxLockupPeriod: bigint
maxLockupPeriod
);