Skip to content

solana-foundation/payment-channels

Repository files navigation

payment-channels

Unidirectional payment channels on Solana: escrow a deposit, authorize cumulative spend with off-chain Ed25519 vouchers, then settle and distribute on-chain. A small Pinocchio program over SPL Token / Token-2022.

Status — live on mainnet: CHNLxYvVA28MJP9PrFuDXccuoGXAx7jBacfLEkahyGsX

Why

One on-chain open and one settle replace a transaction per payment. The payer escrows a ceiling; the merchant claims only what off-chain vouchers authorize; the payer recovers the rest. That makes metered, streamed, or many-small payments viable where settling every request on-chain is too slow and too expensive.

Lifecycle

flowchart LR
  A["open<br/>(escrow a deposit)"] --> B["off-chain vouchers<br/>(cumulative spend)"]
  B --> C["settle<br/>(advance settled amount)"]
  C --> D["distribute<br/>(payee + splits, refund payer)"]
  D --> E["reclaim<br/>(recover channel rent)"]
Loading
stateDiagram-v2
  [*] --> Open: open
  Open --> Open: settle / top_up / distribute (partial)
  Open --> Sealed: settle_and_seal (cooperative)
  Open --> Closing: request_close (forced)
  Closing --> Sealed: settle_and_seal (mid-grace) / seal (after grace)
  Sealed --> Distributed: distribute (pay out, close escrow)
  Distributed --> [*]: reclaim (after epoch window)
  Sealed --> [*]: distribute (fast path, window already elapsed)
Loading

Vouchers are signed off-chain (Ed25519) and carry a cumulative amount, so a newer voucher supersedes older ones and the program never settles more than the deposit. distribute / withdraw_payer move the settled funds out and refund the unspent remainder the moment the channel is sealed — no payout ever waits. The channel account itself is then fully deallocated (directly, or by a later reclaim once the epoch window elapses), returning 100% of its rent: a closed channel leaves nothing on chain.

Used by pay.sh

This program is the on-chain settlement layer behind two pay.sh payment primitives. Both deposit a ceiling here, meter off-chain, and settle the actual amount on this program:

  • x402 upto — a single metered call: the operator settles one voucher for the actual amount and refunds the rest.
  • MPP session — a streamed channel: many cumulative vouchers, settled once when the session idle-closes.

See Payment channels on pay.sh for the protocol handshakes and when to pick each.

Instructions

Instruction Role
open Create the channel PDA and escrow the deposit.
settle Advance the on-chain settled amount from a signed voucher.
settle_and_seal Settle a final voucher and seal in one step (cooperative).
top_up Add funds to an open channel.
request_close Payer-initiated forced close — starts the grace period.
seal Seal a forced-closing channel once the grace period elapses.
distribute Pay the payee and any split recipients; refund the payer; close the escrow.
withdraw_payer Payer recovers the unspent remainder.
reclaim Deallocate a distributed channel and recover its rent (batchable).

Build & test

just setup
just build-program
just generate-client
just test-program

Cluster builds (just build-mainnet-beta, just build-devnet, …) require that cluster's real TREASURY_OWNER in program/payment_channels/src/constants.rs and refuse to compile with the placeholder. No production keypair is committed — pass the program-id keypair explicitly when deploying.

Docs & clients

License

MIT. See LICENSE.

About

🚧 Work in progress. Agentic payments primitive (x402/MPP).

Resources

License

Stars

4 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors