Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"pages": [
"extensions/overview",
"extensions/bazaar",
"extensions/state-channels",
"extensions/payment-identifier",
"extensions/sign-in-with-x",
"extensions/offer-receipt",
Expand Down
186 changes: 186 additions & 0 deletions docs/extensions/state-channels.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
---
title: "State Channels (SCP)"
description: "Off-chain micropayments for x402. Pay thousands of API calls through a single funded channel with zero per-payment gas cost."
---

State channels reduce per-request settlement cost by moving repeated payments off-chain. A payer opens one funded channel, then pays any number of API calls by signing balance updates — no on-chain transaction until the channel closes.

## Route Modes

The `statechannel` scheme supports two route modes, declared via the `route` field in `extensions.statechannel.info`:

| Route | Flow | How it works |
|-------|------|-------------|
| `hub` | Payer → Hub → Payee | One channel, many payees. Hub routes payments. |
| `direct` | Payer → Payee | Direct channel. No intermediary. |

Both preserve standard x402 request/retry semantics — the payee returns `402`, the client signs a payment proof, the client retries.

## When to Use State Channels vs `exact`

| Use case | Recommended scheme |
|----------|-------------------|
| One-off API call, article purchase | `exact` |
| Agent making 100+ API calls per session | `statechannel` (hub route) |
| Pay-per-second media streaming | `statechannel` (hub route) with streaming extension |
| Direct bilateral payment channel | `statechannel` (direct route) |

## Payment Flow (Hub Route)

```
Agent (A) Hub (H) Payee API (B)
│ │ │
│ GET /resource │ │
│─────────────────────────────────────────────────>│
│<─────────────── 402 + offers ────────────────────│
│ │ │
│ POST /v1/tickets/quote │
│─────────────────────>│ │
│<──── quote + draft ──│ │
│ │ │
│ POST /v1/tickets/issue (signed channel state) │
│─────────────────────>│ │
│<── ticket + ack ─────│ │
│ │ │
│ GET /resource + PAYMENT-SIGNATURE ──────────────>│
│<──────────────── 200 + receipt ───────────────────│
```

1. Agent requests a resource, gets a `402` with statechannel offer.
2. Agent asks the hub for a fee quote.
3. Agent signs a channel state update debiting the payment + fee.
4. Hub issues a signed ticket.
5. Agent retries the request with the ticket as payment proof.
6. Payee verifies the ticket and serves the resource.

## 402 Response Example

```json
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https://payee.example/v1/data",
"description": "Premium endpoint",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "statechannel",
"network": "eip155:8453",
"amount": "1000000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "pay.eth",
"maxTimeoutSeconds": 60
}
],
"extensions": {
"statechannel": {
"info": {
"route": "hub",
"hubEndpoint": "https://pay.eth/.well-known/x402",
"mode": "proxy_hold",
"feeModel": {
"base": "10",
"bps": 30
}
},
"schema": {
"type": "object"
}
}
}
}
```

## On-Chain Contract

The `X402StateChannel` contract is the on-chain adjudicator. It handles:

| Function | Purpose |
|----------|---------|
| `openChannel(...)` | Open and fund a new channel |
| `deposit(...)` | Add funds to an existing channel |
| `cooperativeClose(...)` | Instant close — both parties sign the final state |
| `startClose(...)` | Begin unilateral close (starts challenge period) |
| `challenge(...)` | Submit a higher-nonce state during dispute |
| `finalizeClose(...)` | Settle after challenge period expires |
| `rebalance(...)` | Hub moves earned funds between channels atomically |

Canonical deployment via CREATE2: `0x07ECA6701062Db12eDD04bEa391eD226C95aaD4b` (same address on all supported EVM chains).

## Security Model

- **Payer** signs state updates with EIP-712 typed data. Can never be debited more than the signed amount.
- **Hub** routes payments but cannot move funds without a co-signed state. No unilateral custody.
- **Payee** holds a hub-signed ticket as payment proof (hub route) or the latest signed state with on-chain dispute rights (direct route).
- **Unilateral close** includes a challenge period — if one party submits a stale state, the counterparty can submit a newer one.

## Streaming Extension

SCP supports interval-based payment streaming. The payee advertises a tick cadence and per-tick amount:

```json
{
"extensions": {
"statechannel": {
"info": {
"route": "hub",
"stream": {
"amount": "100000000000",
"t": 5
},
"hubEndpoint": "https://pay.eth/.well-known/x402"
},
"schema": { "type": "object" }
}
}
}
```

The client signs a new state update every `t` seconds. The server delivers content and returns stream metadata (`nextCursor`, `hasMore`). When `hasMore` is `false`, the stream ends.

Use cases: pay-per-second audio, pay-per-token LLM generation, metered data feeds.

## Fee Model

Hub fees follow: `fee = base + floor(amount × bps / 10000) + gasSurcharge`

Agents set a `maxFee` ceiling in quote requests. The hub rejects if the computed fee exceeds it.

## Hub API (Reference Implementation)

These endpoints are from the current reference implementation:

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/.well-known/x402` | GET | Hub metadata, supported assets, public key |
| `/v1/tickets/quote` | POST | Get fee quote and payment draft |
| `/v1/tickets/issue` | POST | Submit signed state, receive ticket |
| `/v1/payments/{id}` | GET | Payment status lookup |
| `/v1/channels/{id}` | GET | Channel state lookup |
| `/v1/refunds` | POST | Request a refund |
| `/v1/payee/inbox` | GET | Payee payment inbox |
| `/v1/payee/balance` | GET | Payee balance at hub |
| `/v1/payee/settle` | POST | Request on-chain settlement |

## FAQ

**How does this differ from `exact`?**
`exact` settles each payment on-chain. SCP amortizes cost over a channel lifetime — open once, pay thousands of times off-chain, close once.

**Can SCP run without a hub?**
Yes. Set `route: "direct"` in the offer. The payer opens a channel directly with the payee.

**What if the hub goes offline?**
Both parties can use the on-chain close/challenge paths with the latest signed state. Funds are never locked.

**Is this a custodial wallet?**
No. The hub cannot move funds without the payer's signature. Every balance change requires a co-signed state update.

## Specifications

- Scheme overview: [`specs/schemes/statechannel/scheme_statechannel.md`](https://github.com/coinbase/x402/blob/main/specs/schemes/statechannel/scheme_statechannel.md)
- EVM implementation: [`specs/schemes/statechannel/scheme_statechannel_evm.md`](https://github.com/coinbase/x402/blob/main/specs/schemes/statechannel/scheme_statechannel_evm.md)
- Full SCP specification: [X402S_SPEC_V2.md](https://github.com/Keychain-Inc/x402s/blob/main/docs/X402S_SPEC_V2.md)
- Reference implementation: [github.com/Keychain-Inc/x402s](https://github.com/Keychain-Inc/x402s)
107 changes: 107 additions & 0 deletions specs/schemes/statechannel/scheme_statechannel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Scheme: `statechannel`

## Summary

`statechannel` is a scheme for **off-chain micropayments** backed by on-chain state channels. A payer and counterparty maintain a bidirectional channel funded with ERC-20 tokens (or native ETH) and exchange cryptographically signed balance updates as payment. On-chain transactions are required only for lifecycle events (open, deposit, dispute, close), not for individual payments.

The scheme supports two route modes, declared in the `extensions` object:

| Route | Mode value | Description |
|-------|------------|-------------|
| Hub-routed | `hub` | Payer opens one channel with a hub. Hub routes payments to any registered payee. |
| Direct | `direct` | Payer opens a channel directly with the payee. No intermediary. |

Both modes preserve standard x402 `402` challenge-and-retry semantics. Payees return `402 Payment Required` with statechannel offers; clients construct payment proofs and retry. The only difference from `exact` is that the payment proof is a signed off-chain state update (or hub-issued ticket) instead of a token transfer authorization.

## Use Cases

- **High-frequency AI tool/API calls** — An agent paying $0.001 per API call across dozens of services through a single hub channel, with zero gas cost per call.
- **Metered streaming** — Pay-per-second audio/video, pay-per-token LLM generation, or pay-per-reading IoT sensor data via interval-based ticks.
- **Multi-payee routing** — One funded channel to a hub enables payment to any payee, eliminating per-payee channel overhead.
- **Low-latency "pay and retry"** — Payment completes in a single HTTP round-trip with no on-chain confirmation wait.

## How It Differs from `exact`

| Property | `exact` | `statechannel` |
|----------|---------|----------------|
| On-chain transactions per payment | 1 | 0 (amortized over channel lifetime) |
| Payments before on-chain settlement | 1 | Unlimited (bounded by channel balance) |
| Latency per payment | Block confirmation time | Single HTTP round-trip |
| Minimum viable payment | Gas-bound (~$0.01+) | Arbitrary (1 wei+) |
| Client state | Stateless | Stateful (channel nonce, balance) |
| Trust model | Trustless (on-chain transfer) | Trust-minimizing (off-chain with on-chain dispute) |

## Trust Model

### Hub Route (`hub`)

- **Payer risk:** Bounded by the signed debit amount plus `maxFee`. The payer never authorizes more than they explicitly sign.
- **Payee risk:** Bounded by hub signature validity and ticket expiry. Payee trusts that the hub will settle according to ticket terms.
- **Hub risk:** Protected by valid channel state updates proving the payer's debit authorization. Hub cannot move funds without a co-signed state.

### Direct Route (`direct`)

- **Payer risk:** Same as hub-routed — bounded by signed debit.
- **Payee risk:** Payee has on-chain dispute rights. Can submit the highest-nonce signed state to the channel contract for settlement.

## Core Security Requirements

Implementations MUST enforce:

1. **Nonce monotonicity** — `stateNonce` MUST strictly increase for each accepted update per channel.
2. **Balance conservation** — `balA + balB` MUST equal the channel's on-chain `totalBalance`.
3. **Signature binding** — Every state update MUST be verified via EIP-712 typed data recovery, binding the signer to the channel, nonce, and balance split.
4. **Expiry enforcement** — Quotes, tickets, and state updates with elapsed expiry MUST be rejected.
5. **Bounded debit** — Each payment MUST debit no more than `amount + disclosed fees` from the payer's balance.
6. **Replay protection** — `channelId` (scoped to chain + contract + participants + salt) plus `stateNonce` plus EIP-712 `chainId` prevent cross-channel and cross-chain replay. `paymentId`/`invoiceId` identifiers MUST be idempotent when present.
7. **Challenge-period dispute** — Unilateral close MUST include a challenge window during which the counterparty can submit a higher-nonce state.

## Extension Metadata

The `statechannel` scheme uses the standard x402 v2 `extensions` object under the key `"statechannel"` to convey route-specific configuration.

### Common Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `route` | `"hub"` \| `"direct"` | Yes | Route mode for this offer |

### Hub Route Fields (`route: "hub"`)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `hubEndpoint` | string (URL) | Yes | Hub metadata discovery URL (e.g., `https://pay.eth/.well-known/x402`). Clients fetch this to discover hub capabilities, then derive API routes (`/v1/tickets/quote`, `/v1/tickets/issue`, etc.) from the same origin. |
| `mode` | string | No | Settlement policy hint. Default: `"proxy_hold"` |
| `feeModel` | object | No | `{ "base": "<uint>", "bps": <int> }` — hub fee formula |
| `quoteExpiry` | number | No | Unix timestamp when quote expires |

### Direct Route Fields (`route: "direct"`)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `payeeAddress` | EVM address | Yes | Payee's on-chain address (channel counterparty) |
| `challengePeriodSec` | number | No | Challenge window duration for dispute |

Servers SHOULD offer at least one fallback scheme (e.g., `exact`) alongside statechannel offers during rollout. Servers MAY include multiple statechannel offers with different route modes in the same `accepts[]` array.

## Critical Validation Requirements

While implementation details vary by network, verifiers MUST enforce security constraints that prevent fund theft and replay:

### EVM

- **Signer recovery:** The recovered signer from the EIP-712 digest MUST match the expected payer (`participantA` for hub route, channel counterparty for direct).
- **Nonce ordering:** `stateNonce` MUST be strictly greater than the last accepted nonce for the same channel.
- **Balance conservation:** `balA + balB` MUST equal the channel's on-chain `totalBalance`.
- **Debit correctness:** The balance delta (`previous.balA - current.balA`) MUST be greater than or equal to `accepted.amount`.
- **Expiry validity:** States with `stateExpiry > 0 && now > stateExpiry` MUST be rejected.
- **Ticket binding (hub route):** `channelProof.stateHash` MUST equal the hash of the submitted channel state. If `ticket.stateHash` is present, it MUST match `channelProof.stateHash`. `ticket.sig` MUST recover to the hub's advertised address.

Network-specific rules are in: `scheme_statechannel_evm.md` (EVM).

## Appendix

- Core protocol types: [`x402-specification-v2.md`](../../x402-specification-v2.md)
- EVM payload, verification, and settlement: [`scheme_statechannel_evm.md`](./scheme_statechannel_evm.md)
- Reference implementation: [github.com/Keychain-Inc/x402s](https://github.com/Keychain-Inc/x402s)
- Full SCP specification: [X402S_SPEC_V2.md](https://github.com/Keychain-Inc/x402s/blob/main/docs/X402S_SPEC_V2.md)
Loading
Loading