payments

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package payments provides the payment management service for Filecoin Onchain Cloud.

It handles USDFC token operations including deposits, withdrawals, balance queries, approval management, and payment rail creation via the Filecoin Pay contract.

The root synapse Client wires payments together with the other write-capable services so transaction nonce allocation is coordinated for a shared signer. Standalone services create their own nonce coordinator when constructed with write dependencies.

Stability

0.x phase: public API may change between minor releases.

Example

Example shows reading an account balance via payments.Service. In practice a Service is obtained from synapse.Client.Payments.

package main

import (
	"context"
	"fmt"
	"log"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/strahe/synapse-go/payments"
)

func main() {
	var svc *payments.Service // obtained from synapse.Client.Payments()

	ctx := context.Background()
	usdfc := common.HexToAddress("0x...")
	owner := common.HexToAddress("0x...")

	bal, err := svc.Balance(ctx, usdfc, owner)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(new(big.Int).Set(bal))
}

Index

Examples

Constants

View Source
const PermitDeadlineDuration = time.Hour

PermitDeadlineDuration is the default validity window appended to the current wall-clock time when the caller does not provide an explicit permit deadline.

Variables

View Source
var ErrClosed = lifecycle.ErrClosed

ErrClosed is returned when a method is called after the owning Client has been closed. It aliases the shared closed-client sentinel, so errors.Is(err, payments.ErrClosed) matches either sentinel.

View Source
var ErrInsufficientAllowance = errors.New("payments: insufficient allowance")

ErrInsufficientAllowance is returned when an ERC20 allowance is lower than the amount required for a deposit.

View Source
var ErrInsufficientBalance = errors.New("payments: insufficient balance")

ErrInsufficientBalance is returned when the on-chain balance is lower than the requested amount for a withdrawal or transfer.

View Source
var ErrInvalidArgument = errors.New("payments: invalid argument")

ErrInvalidArgument is returned, wrapped via fmt.Errorf with %w, when a caller passes an argument that violates a precondition (nil pointer, zero address, invalid chain ID). Match with errors.Is(err, payments.ErrInvalidArgument).

Business-domain sentinels such as ErrInsufficientBalance or ErrInsufficientAllowance live alongside this file and cover runtime preconditions that are independent of caller-supplied arguments.

View Source
var ErrNothingToFund = errors.New("payments: nothing to fund (already approved and amount is 0)")

ErrNothingToFund is returned by Fund / FundSync when the account is already fully approved for WarmStorage and the caller requested a zero deposit amount, meaning there is no work to do.

View Source
var ErrPermitUnsupported = errors.New("payments: token contract does not support EIP-2612 permit")

ErrPermitUnsupported is returned by DepositWithPermit and DepositWithPermitAndApproveOperator when the token contract does not expose the EIP-2612 permit ABI (name / version / nonces probes fail). Callers can branch on this with errors.Is to fall back to Approve + Deposit instead of surfacing a generic precondition error.

View Source
var ErrTxFailed = types.ErrTxFailed

ErrTxFailed reports that a transaction was mined but reverted on-chain. Use errors.Is to match errors returned by state-changing calls.

This is an alias of types.ErrTxFailed kept for backwards compatibility; callers can match either interchangeably.

View Source
var ErrUninitialized = errors.New("payments: service not initialized; use payments.New")

ErrUninitialized is returned when a method is invoked on a zero-value Service (one that was not constructed via New). Match with errors.Is(err, payments.ErrUninitialized).

View Source
var ErrZeroAddress = errors.New("payments: zero address")

ErrZeroAddress is returned when a caller passes common.Address{} for an argument that must be a real token or account address. Public service methods wrap it together with ErrInvalidArgument so callers can match either the specific zero-address sentinel or the generic invalid-argument sentinel.

View Source
var LockupPeriodEpochs = big.NewInt(30 * 2880)

LockupPeriodEpochs is the default client-operator max lockup period granted when Fund auto-approves the WarmStorage operator: 30 days at 2880 epochs per day.

View Source
var ZeroAddress = common.Address{}

ZeroAddress is a convenience alias for common.Address{} used to indicate native FIL in WalletBalance queries.

Functions

This section is empty.

Types

type AccountState

type AccountState struct {
	Funds               *big.Int
	LockupCurrent       *big.Int
	LockupRate          *big.Int
	LockupLastSettledAt *big.Int
	// FundedUntilEpoch is the forward-looking epoch at which the account's
	// available funds will be exhausted at the current lockup rate.
	// Sourced from getAccountInfoIfSettled(); zero when LockupRate is zero.
	FundedUntilEpoch *big.Int
	// contains filtered or unexported fields
}

AccountState mirrors FilPay.accounts(token, owner). All values are in base units of the payment token.

func (*AccountState) AvailableFunds

func (a *AccountState) AvailableFunds() *big.Int

AvailableFunds returns Funds - LockupCurrent (never negative). A nil AccountState returns nil.

type Backend

type Backend interface {
	bind.ContractBackend
	BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
	TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
	BlockNumber(ctx context.Context) (uint64, error)
}

Backend is the minimal RPC surface used by the payments service. It is satisfied by *ethclient.Client. Tests can substitute a mock.

type ListOption

type ListOption func(*listConfig)

ListOption tunes paginated list calls.

func WithListLimit

func WithListLimit(limit *big.Int) ListOption

WithListLimit caps the number of results returned in one page. limit == 0 requests all remaining rails; negative values are ignored.

func WithListOffset

func WithListOffset(offset *big.Int) ListOption

WithListOffset sets the starting offset for paginated results.

type OperatorApproval

type OperatorApproval struct {
	IsApproved      bool
	RateAllowance   *big.Int // maximum per-epoch rate the operator may charge
	LockupAllowance *big.Int // maximum lockup the operator may hold
	RateUsage       *big.Int // current per-epoch rate in use by the operator
	LockupUsage     *big.Int // current lockup held by the operator
	MaxLockupPeriod *big.Int // maximum lockup period allowed (in epochs)
}

OperatorApproval mirrors FilPay.operatorApprovals(token, client, operator).

type Options

type Options struct {
	// Backend is the Ethereum RPC client. Required.
	Backend Backend
	// ChainID of the target FEVM chain. Required; must be > 0.
	ChainID sdktypes.ChainID
	// FilPayAddress is the Filecoin Pay contract address. Required.
	FilPayAddress common.Address
	// WarmStorageAddress is the FilecoinWarmStorageService contract address.
	// Optional; required for Fund / FundSync smart-route helpers because
	// they route through WarmStorage operator approval state. When zero
	// Fund / FundSync return ErrInvalidArgument.
	WarmStorageAddress common.Address
	// USDFCTokenAddress is the USDFC (or other ERC-2612) token address
	// used as the default for Fund / FundSync and for permit operations
	// that do not receive an explicit token argument. Optional; callers
	// may always pass a token explicitly to DepositWithPermit.
	USDFCTokenAddress common.Address
	// Signer is used to sign transactions. Required for write methods;
	// may be nil when the Service is used for reads only.
	Signer signer.EVMSigner
	// Logger is optional. When nil, logging is disabled.
	Logger *slog.Logger
	// NonceManager is optional. The root synapse Client injects a shared
	// coordinator across all write-capable services; standalone callers may
	// leave this nil to create one for this Service.
	NonceManager *txutil.NonceManager
	// ReceiptWait overrides the default receipt polling timeout used by
	// WithConfirmations when the call waits for a receipt but does not
	// provide a more specific WithWait(timeout). Zero uses
	// txutil.DefaultReceiptWaitConfig.
	ReceiptWait time.Duration
	// Lifecycle, when non-nil, ties this Service to the owning Client's
	// close state. After the Lifecycle is closed, every method returns
	// ErrClosed without touching the RPC backend. Nil is allowed for
	// standalone use.
	Lifecycle *lifecycle.Lifecycle
}

Options bundles the dependencies for constructing a Service.

type PermitOption

type PermitOption func(*permitConfig)

PermitOption is reserved for future nonce / salt overrides. Currently unused; keep the type to preserve API stability once options land.

type RailListItem

type RailListItem struct {
	RailID       sdktypes.BigInt
	IsTerminated bool
	EndEpoch     *big.Int
}

RailListItem is a single entry returned by GetRailsAsPayer / GetRailsAsPayee; it corresponds to `FilecoinPayV1RailInfo` on the contract side.

type RailPage

type RailPage struct {
	Rails      []RailListItem
	NextOffset *big.Int
	Total      *big.Int
}

RailPage is one page of rails plus pagination cursors.

type RailView

type RailView struct {
	Token               common.Address
	From                common.Address
	To                  common.Address
	Operator            common.Address
	Validator           common.Address
	PaymentRate         *big.Int
	LockupPeriod        *big.Int
	LockupFixed         *big.Int
	SettledUpTo         *big.Int
	EndEpoch            *big.Int
	CommissionRateBps   *big.Int
	ServiceFeeRecipient common.Address
}

RailView is the flattened view of FilecoinPayV1RailView from the FilPay contract.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides read and write access to the Filecoin Pay contract plus convenience wrappers around ERC20 allowance management.

It is safe for concurrent use. All state-changing calls return a sdktypes.WriteResult whose Receipt is only populated when WithWait is supplied.

func New

func New(opts Options) (*Service, error)

New constructs a Service.

func (*Service) Account

func (s *Service) Account() common.Address

Account returns the EOA address used for writes, or the zero address when the service was constructed without a signer.

func (*Service) AccountInfo

func (s *Service) AccountInfo(ctx context.Context, token, owner common.Address) (*AccountState, error)

AccountInfo returns the on-chain account record for (token, owner).

When token is ZeroAddress, native FIL is queried via BalanceAt and returned as AccountState.Funds. All lockup fields are zero because native FIL is not tracked by the FilPay contract.

func (*Service) Address

func (s *Service) Address() common.Address

Address returns the FilPay contract address.

func (*Service) Allowance

func (s *Service) Allowance(ctx context.Context, token, owner, spender common.Address) (*big.Int, error)

Allowance returns the ERC20 allowance of owner towards spender for token.

func (*Service) Approve

func (s *Service) Approve(ctx context.Context, token, spender common.Address, amount *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

Approve calls ERC20.approve(spender, amount) on the given token.

spender is typically the FilPay contract address; use service.Address() for that convenience.

func (*Service) ApproveService

func (s *Service) ApproveService(ctx context.Context, token, operator common.Address, rateAllowance, lockupAllowance, maxLockupPeriod *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

ApproveService calls FilPay.setOperatorApproval(token, operator, true, rateAllowance, lockupAllowance, maxLockupPeriod). Use RevokeService to clear the approval.

func (*Service) Balance

func (s *Service) Balance(ctx context.Context, token, owner common.Address) (*big.Int, error)

Balance is a convenience that returns the Funds field of AccountInfo.

func (*Service) ChainID

func (s *Service) ChainID() sdktypes.ChainID

ChainID returns the configured chain id.

func (*Service) Deposit

func (s *Service) Deposit(ctx context.Context, token, to common.Address, amount *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

Deposit calls FilPay.deposit(token, to, amount). The caller must have first approved at least `amount` on the token contract for FilPay. When `to` is the zero address the caller's EOA is used.

func (*Service) DepositWithPermit

func (s *Service) DepositWithPermit(
	ctx context.Context,
	token, to common.Address,
	amount, deadline *big.Int,
	opts ...WriteOption,
) (*sdktypes.WriteResult, error)

DepositWithPermit deposits `amount` of `token` into the Filecoin Pay contract on behalf of the signer in a single on-chain transaction by attaching an ERC-2612 permit signature. No prior ERC20 approval is required.

`to` is the credited depositor; when zero, defaults to the signer EOA. `deadline` is the permit expiry (unix seconds); when nil, now + PermitDeadlineDuration is used. `token` must be an ERC-2612 / ERC20Permit-compliant token (e.g. USDFC).

func (*Service) DepositWithPermitAndApproveOperator

func (s *Service) DepositWithPermitAndApproveOperator(
	ctx context.Context,
	token, to common.Address,
	amount, deadline *big.Int,
	operator common.Address,
	rateAllowance, lockupAllowance, maxLockupPeriod *big.Int,
	opts ...WriteOption,
) (*sdktypes.WriteResult, error)

DepositWithPermitAndApproveOperator combines DepositWithPermit with a SetOperatorApproval in the same transaction. Use this to onboard a new client: deposit + grant WarmStorage (or another operator) the allowances it needs in a single click.

func (*Service) Fund

func (s *Service) Fund(ctx context.Context, amount *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

Fund is a smart deposit that auto-detects whether WarmStorage is already approved with sufficient allowances and routes to the correct on-chain call:

  • needs approval + amount > 0 → DepositWithPermitAndApproveOperator
  • needs approval + amount == 0 → ApproveService
  • already approved + amount > 0 → DepositWithPermit
  • already approved + amount == 0 → ErrNothingToFund

Fund requires WarmStorageAddress and USDFCTokenAddress to be set on the Options. Returns ErrInvalidArgument when either is zero.

amount must be non-nil. Callers that want an approval-only flow must pass big.NewInt(0) explicitly so an omitted amount cannot silently broadcast an approval transaction.

Pass WithFundNeedsFwssApproval to reuse a previously computed approval decision instead of re-reading on-chain state.

func (*Service) FundSync

func (s *Service) FundSync(ctx context.Context, amount *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

FundSync runs Fund and waits for the transaction to be mined. It is equivalent to Fund(..., WithWait(timeout)) with a sensible default timeout when the caller did not supply one.

func (*Service) GetRail

func (s *Service) GetRail(ctx context.Context, railID sdktypes.BigInt) (*RailView, error)

GetRail returns the full view of a single rail by id.

func (*Service) GetRailsAsPayee

func (s *Service) GetRailsAsPayee(ctx context.Context, payee, token common.Address, opts ...ListOption) (*RailPage, error)

GetRailsAsPayee lists rails where `payee` is the account being paid on `token`. Callers may paginate with WithListOffset / WithListLimit.

func (*Service) GetRailsAsPayer

func (s *Service) GetRailsAsPayer(ctx context.Context, payer, token common.Address, opts ...ListOption) (*RailPage, error)

GetRailsAsPayer lists rails where `payer` is the account being charged for `token`. Callers may paginate with WithListOffset / WithListLimit.

func (*Service) GetSettlementAmounts

func (s *Service) GetSettlementAmounts(ctx context.Context, railID sdktypes.BigInt, untilEpoch *big.Int) (*SettlementResult, error)

GetSettlementAmounts simulates FilPay.settleRail via eth_call and decodes the resulting amounts without broadcasting a transaction. Use it to preview how much would be settled to the payee / operator / network.

func (*Service) RevokeService

func (s *Service) RevokeService(ctx context.Context, token, operator common.Address, opts ...WriteOption) (*sdktypes.WriteResult, error)

RevokeService clears a prior ApproveService by setting approved=false and all allowances to zero.

func (*Service) ServiceApproval

func (s *Service) ServiceApproval(ctx context.Context, token, client, operator common.Address) (*OperatorApproval, error)

ServiceApproval returns the operator approval record for (token, client, operator).

func (*Service) Settle

func (s *Service) Settle(ctx context.Context, railID sdktypes.BigInt, untilEpoch *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

Settle triggers a rail settlement up to `untilEpoch`. When `untilEpoch` is nil or zero, the current block number is used.

func (*Service) SettleAuto

func (s *Service) SettleAuto(ctx context.Context, railID sdktypes.BigInt, untilEpoch *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

SettleAuto inspects the rail state and routes to Settle or SettleTerminatedRail automatically — terminated rails (endEpoch > 0) go through the emergency path, active rails through the standard path.

func (*Service) SettleTerminatedRail

func (s *Service) SettleTerminatedRail(ctx context.Context, railID sdktypes.BigInt, opts ...WriteOption) (*sdktypes.WriteResult, error)

SettleTerminatedRail triggers the emergency-settlement path for a terminated rail. This bypasses the operator validator and pays in full; it can only be called by the client after the max settlement epoch has passed.

func (*Service) WalletBalance

func (s *Service) WalletBalance(ctx context.Context, token, account common.Address) (*big.Int, error)

WalletBalance returns the EOA balance of token. When token is the zero address the native FIL balance is returned via BalanceAt. Otherwise the ERC20 balanceOf(account) is queried.

func (*Service) Withdraw

func (s *Service) Withdraw(ctx context.Context, token common.Address, amount *big.Int, opts ...WriteOption) (*sdktypes.WriteResult, error)

Withdraw calls FilPay.withdraw(token, amount). The amount must not exceed AccountInfo.AvailableFunds (pre-check can be disabled via WithSkipPrecheck).

type SettlementResult

type SettlementResult struct {
	TotalSettledAmount      *big.Int
	TotalNetPayeeAmount     *big.Int
	TotalOperatorCommission *big.Int
	TotalNetworkFee         *big.Int
	FinalSettledEpoch       *big.Int
	Note                    string
}

SettlementResult is the decoded return tuple of FilPay.settleRail.

type WriteOption

type WriteOption func(*writeConfig)

WriteOption tunes the behaviour of a single state-changing call.

func WithConfirmations

func WithConfirmations(n uint64) WriteOption

WithConfirmations requires N block confirmations in addition to WithWait. Has no effect unless WithWait is also passed with a positive timeout.

func WithFundNeedsFwssApproval

func WithFundNeedsFwssApproval(needs bool) WriteOption

WithFundNeedsFwssApproval overrides Fund / FundSync's approval-state probe. When set, Fund skips isFwssMaxApproved and uses the supplied decision directly so prepare/execute flows can reuse a previously computed result.

func WithSkipPrecheck

func WithSkipPrecheck() WriteOption

WithSkipPrecheck disables client-side balance / allowance / funds checks before broadcasting. Useful when the caller has already validated state or wants to probe an on-chain revert for diagnostic purposes.

func WithWait

func WithWait(timeout time.Duration) WriteOption

WithWait makes the call block until the transaction is mined, or the given timeout elapses. Use zero or a negative duration to return as soon as the tx is broadcast (the default).

type WriteResult

type WriteResult = sdktypes.WriteResult

WriteResult is kept as an alias for backwards compatibility.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL