Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

374 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

payment-channels

Payment channels make agentic payments practical by escrowing a spending ceiling on-chain and settling actual usage from signed vouchers; this program backs MPP session for streaming or repeated payments and x402 upto for one metered request.

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/>(create PDA + escrow deposit)"] --> B["off-chain vouchers<br/>(cumulative spend)"]
  B --> C["Ed25519 precompile + settle<br/>(advance settled watermark)"]
  C --> D["settle_and_seal<br/>(lock final watermark)"]
  D --> E["distribute<br/>(payouts + refund + close escrow)"]
  E --> F["reclaim, when needed<br/>(recover channel-PDA 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 pays the settled amount; once the channel is sealed, distribute or withdraw_payer can refund the unspent remainder immediately—no token movement waits on the epoch window. The channel account itself is then fully deallocated (directly, or by a later reclaim once the window elapses), returning 100% of its rent: a closed channel leaves nothing on chain.

Architecture

  • The Pinocchio program stores each channel in a 256-byte PDA derived from b"channel", payer, payee, mint, authorized_signer, salt, and open_slot. Its escrow is the canonical ATA owned by that PDA.
  • open_slot makes every channel incarnation land at a new address. open accepts only a current-or-recent slot (OPEN_SLOT_WINDOW = 1,500), and the program keeps the PDA allocated until that window has elapsed. An old voucher therefore cannot target a later incarnation.
  • Channel fields—not raw token or lamport balances—are authoritative. Third-party token prefunds are uncredited and swept to treasury by the terminal distribute; surplus PDA lamports return to rent_payer when the account is deallocated.
  • open stores only the SHA-256 commitment to the canonical distribution preimage. Any permissionless distribute caller supplies the preimage and recipient ATAs.
  • rent_payer can differ from the token payer, allowing an operator to fund SOL rent for a stablecoin-only client and recover that SOL at close.

Voucher wire format

The Ed25519-signed message is exactly 50 bytes:

Offset Size Field Encoding
0..2 2 magic [0x56, 0x01] ('V', format version 1)
2..34 32 channel_id Channel PDA bytes
34..42 8 cumulative_amount u64, little-endian
42..50 8 expires_at Unix timestamp as i64, little-endian; 0 disables expiry

settle carries no voucher copy in its own instruction data. The transaction places a canonical single-signature Ed25519 precompile instruction immediately before settle (or before voucher-bearing settle_and_seal); the program reads the verified 50-byte message through the Instructions sysvar. See the state-machine voucher contract for replay, expiry, and signer checks.

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 the preceding Ed25519-verified 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 cumulative recipient/payee shares while open; when sealed, refund the payer, sweep residuals, and 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.

Future work: batch voucher settlement

A proposed settleBatch instruction would let one Ed25519 voucher authorize cumulative targets for many channels that share an authorized_signer. The signed message would commit to the ordered channel-account list and carry one amount per channel, avoiding repeated signatures and channel addresses. A version-0 transaction with an address lookup table could then settle roughly 59 channels instead of about five, while preserving per-channel caps and replay checks. This is not implemented and requires a new voucher wire format; see ADR-004 for the adversarial analysis and trade-offs.

Security audit

The program was audited by Cantina. Read the July 2026 security audit.

About

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

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages