Skip to main content
This page explains the protocol-level building blocks of the Flex scheme. Implementation details for the Solana program live in the Program Reference.

Participants

RoleResponsibility
ClientFunds the escrow, signs payment authorizations off-chain with a session key.
MiddlewareGenerates payment requirements, gates the resource, and negotiates a hold with the client.
FacilitatorValidates authorizations, runs settlement policy, and submits authorizations on-chain.
The middleware and facilitator can be the same service or separate services that communicate via API. Faremeter middleware delegates verify/settle to a facilitator over HTTP.

Escrow accounts

An escrow is a prepaid balance held by the on-chain program on behalf of a single client and a single facilitator. It can hold multiple token mints. Each escrow is parameterized by:
  • refund_timeout_slots — how long a pending settlement waits before finalizing. Bounded between ~150 slots (~1 minute) and ~1,296,000 slots (~6 days).
  • deadman_timeout_slots — how long the facilitator can be inactive before the client can unilaterally close. Bounded between ~1,000 slots (~6.7 minutes) and ~2,592,000 slots (~12 days). Must be at least 2 * refund_timeout_slots.
  • max_session_keys — caps the number of registered session keys. 0 means unlimited.
A client creates a separate escrow per facilitator they work with. The PDA also includes a numeric index, so a single client may hold multiple escrows with the same facilitator. Once created, the escrow contract requires dual authorization for any transfer out: the client’s session-key signature on a payment authorization, plus an on-chain transaction signature from the registered facilitator. Neither party can move funds unilaterally.

Session keys

Session keys are Ed25519 keypairs that the client registers on-chain to sign payment authorizations off-chain. They exist so that:
  • The client never needs to sign with the escrow account’s private key on every request.
  • Smart wallets, multisigs, and custodial wallets work natively. None of them can sign arbitrary off-chain messages, but they can sign the on-chain transaction that registers a session key.
  • Keys can be revoked unilaterally by the client. A configurable grace period (revocation_grace_period_slots) lets the facilitator settle in-flight authorizations after revocation but before the key becomes fully invalid.
Session keys carry no spending limits of their own. Limits are expressed per-authorization. If you need to bound exposure, fund a smaller escrow or use a key with a short expires_at_slot.

Payment authorizations

A payment authorization is a client-signed message that authorizes a single payment from the escrow. The signed fields are:
FieldDescription
programIdThe Flex program ID (binds the signature to a specific deployment).
escrowEscrow PDA the payment is drawn from.
mintSPL token mint to settle in.
maxAmountCeiling the facilitator may settle up to. The actual settle_amount can be lower.
authorizationIdRandom u64 identifier; provides on-chain replay protection via PDA seeds.
expiresAtSlotSlot after which the authorization may not be submitted.
splitsRecipient list with basis-point allocations summing to 10,000.
The facilitator can only submit what the client signed. They cannot raise the amount, change the mint, or alter the split distribution.

Splits

Splits encode multi-recipient settlement directly into the signed authorization. Each entry is (recipient_token_account, bps) and the entries must sum to exactly 10000 (100%). A single-recipient payment is a one-entry split with 10000 bps. Splits enable platform fees, referral commissions, royalties, and facilitator fees in a single atomic settlement. The on-chain program distributes funds proportionally at finalize time. The current Solana program supports up to 5 splits per authorization.

Holds

When a request arrives, the middleware and facilitator coordinate a hold before any work is done:
  1. Hold request. The middleware tells the client the maximum amount the operation may consume.
  2. Client authorization. The client signs an authorization for maxAmount (which may be larger than the requested amount, leaving headroom for variable cost).
  3. Hold validation. The middleware forwards the signed authorization to the facilitator. The facilitator validates the signature, confirms sufficient escrow capacity, and reserves it in memory.
  4. Service delivery. With the hold reserved, the middleware performs the work.
  5. Settlement. The middleware reports the actual amount consumed. The facilitator converts the in-memory hold to an on-chain pending settlement at the actual amount used.
Holds are tracked off-chain by the facilitator in memory. They keep the in-flight commitments visible without burning a transaction per request. When the facilitator decides to submit an authorization on-chain, it picks the actual amount used (which must be <= maxAmount) and turns the hold into a pending settlement.

Pending settlements and the refund window

A submit_authorization instruction creates a PendingSettlement PDA with:
  • amount — the actual amount being settled (the hold’s settleAmount, not the ceiling).
  • original_amount and max_amount — preserved for audit.
  • submitted_at_slot and expires_at_slot.
The pending settlement can be modified or finalized as follows:
  • Refund window. Until submitted_at_slot + refund_timeout_slots elapses, the facilitator may call refund to reduce or cancel the amount. This is how the system handles failed deliveries or disputes the facilitator agrees with.
  • Finalize. After the refund window closes, anyone can call finalize to distribute funds per the signed splits and close the pending settlement PDA. The rent for the PDA is returned to the facilitator.
  • Void on deadman. If the deadman switch fires, all pending settlements are voided and the funds remain in the escrow.
The refund window is enforced on-chain, not by facilitator policy. The merchant is guaranteed to be paid after it closes (assuming sufficient escrow balance), and the client is guaranteed recourse during it. The on-chain program caps the number of concurrent pending settlements per escrow at MAX_PENDING_SETTLEMENTS = 16. Facilitators must finalize older settlements before submitting new ones once the cap is reached.

Deadman switch

If the facilitator becomes unresponsive — no submit_authorization, no refund — the client can recover funds without facilitator cooperation once last_activity_slot + deadman_timeout_slots has elapsed. The recovery is a three-step sequence:
  1. void_pending for each open pending settlement. Either the owner or the facilitator can call this once the deadman timer has expired (or once a pending settlement has been sitting past submitted_at_slot + refund_timeout_slots + deadman_timeout_slots). Funds reserved behind the pending PDA return to the vault.
  2. revoke_session_key + close_session_key for each registered session key. emergency_close requires session_key_count == 0.
  3. emergency_close — owner-only. Drains all vault token accounts to owner-controlled destinations and closes the escrow PDA.
last_activity_slot is only updated by instructions that require the facilitator’s signature (submit_authorization, refund). Permissionless instructions like finalize and deposit do not reset the timer. This prevents a malicious facilitator from holding funds hostage by cranking finalizations while ignoring new business. The deadman switch guarantees that funds are never permanently locked, regardless of facilitator behavior. Choose a deadman_timeout_slots that balances client patience against the facilitator’s legitimate downtime tolerance.

Replay protection and expiry

Each authorization carries a unique authorization_id. The pending-settlement PDA seeds include this ID, so a duplicate submit_authorization fails at account initialization. expires_at_slot provides a second layer: an authorization that the facilitator delays past its expiry can no longer be submitted. The on-chain program also bounds how far in the future an expiry may be: at submission time, expires_at_slot must be <= current_slot + escrow.refund_timeout_slots. This caps a stale authorization’s settlement window to the same horizon as the refund window.

Security model at a glance

Compromised partiesSeverityWhat attacker can do
Middleware aloneNoneCannot sign; can only forward authorizations the client signed.
Session key aloneLowCan sign authorizations, but cannot submit them without facilitator co-op.
Session key + facilitatorCriticalCan drain up to signed maxAmount per authorization via fraudulent splits.
Recommended mitigations: confirm split recipients before signing, use short-lived session keys, fund escrows based on expected usage, and monitor on-chain pending settlements. See the security note in the Program Reference.

Further reading

  • Quickstart — end-to-end client flow with @faremeter/flex-solana.
  • Facilitator — operator-side hold management and settlement.
  • Program Reference — on-chain instructions, account layouts, and constants.
  • Payment Schemes — where Flex sits among Faremeter’s other schemes.