Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0692bb0
Rename scheme: commerce -> authCapture
A1igator Apr 28, 2026
443b101
authCapture: new wire format, salt on payload, autoCapture, Permit2
A1igator Apr 28, 2026
3276d3a
Address review: charge ABI, expiry-ordering check, ASSET_INFO scope, …
A1igator Apr 28, 2026
77bb371
Round-2 review fixes: typed reverts, fork tests, all out-of-scope cle…
A1igator Apr 28, 2026
a3f33ea
Round-3 review: fork-test setup graceful skip, post-state assertions,…
A1igator Apr 29, 2026
a6758ac
Align with fabrice's spec proposal: minFeeBps required, maxTimeoutSec…
A1igator Apr 29, 2026
875a499
captureAuthorizer description: include authorize + charge
A1igator Apr 29, 2026
f999189
captureAuthorizer scope: facilitator EOA or contract, not the merchant
A1igator Apr 29, 2026
70eda1e
captureAuthorizer: any smart contract; merchant via contract, not EOA
A1igator Apr 29, 2026
45d6a5c
captureAuthorizer: drop redundant "not merchant EOA" note
A1igator Apr 29, 2026
7fc4eec
captureAuthorizer wording: drop "every method" + per-network parenthe…
A1igator Apr 29, 2026
5acf558
captureAuthorizer: drop PaymentOperator example from spec
A1igator Apr 29, 2026
ed69c06
captureAuthorizer wording: "on-chain settle call" -> "\"Authorize\" c…
A1igator Apr 29, 2026
80ba1a8
captureAuthorizer table: drop msg.sender clause
A1igator Apr 29, 2026
924c672
authCapture: update canonical addresses + 14-chain coverage
A1igator Apr 30, 2026
b44821a
authCapture: add BSC + Tempo as Permit2-only chains
A1igator Apr 30, 2026
0136a88
spec: split out to a separate PR
A1igator Apr 30, 2026
0a07454
authCapture: enforce permit2Only via defaultMoneyConversion + cite v1…
A1igator May 1, 2026
449971a
authCapture: relax refundDeadline ordering to >= for contract parity
A1igator May 1, 2026
31ac364
authCapture: drop chain-based assetTransferMethod auto-injection
A1igator May 1, 2026
d69e3cd
Merge main: bump @x402r/evm 0.0.3 → 0.2.0
A1igator May 1, 2026
b456690
authCapture: drop unused MAX_UINT exports + rename stale test fixtures
A1igator May 5, 2026
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
13 changes: 7 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# x402r-scheme

Commerce scheme bridging x402 protocol with x402r commerce contracts.
authCapture scheme bridging the x402 protocol with the canonical AuthCaptureEscrow contracts (base/commerce-payments).

## Commands

Expand All @@ -13,13 +13,14 @@ pnpm build / pnpm test / pnpm format
Single package `@x402r/evm` at `packages/evm/`:

```
commerce/client → createPaymentPayload()
commerce/server → CommerceServerScheme
commerce/facilitator → Settlement and verification
authCapture/client → createPaymentPayload()
authCapture/server → AuthCaptureServerScheme
authCapture/facilitator → Settlement and verification
authCapture/shared → types, constants, nonce + signing helpers
```

## Dependencies

- Types from `@x402/core` and `@x402/evm` (base x402 protocol)
- Calls on-chain AuthCaptureEscrow and PaymentOperator (commerce-payments)
- Extends base x402 scheme pattern (x402/)
- Calls on-chain AuthCaptureEscrow directly (canonical universal CREATE2 deploy from base/commerce-payments)
- Extends base x402 scheme pattern
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# x402r-scheme

Commerce payment scheme for x402 using Base Commerce Payments.
AuthCapture payment scheme for x402 using Base Commerce Payments.

## Packages

- **[@x402r/evm](./packages/evm)** - Commerce scheme implementation for EVM chains
- **[@x402r/evm](./packages/evm)** - AuthCapture scheme implementation for EVM chains

## Installation

Expand All @@ -19,36 +19,39 @@ Peer dependencies: `@x402/core`, `@x402/evm`, `viem`
### Client

```typescript
import { CommerceEvmScheme, registerCommerceEvmScheme } from '@x402r/evm/commerce/client'
import { AuthCaptureEvmScheme, registerAuthCaptureEvmScheme } from '@x402r/evm/authCapture/client'
import { x402Client } from '@x402/core/client'

const client = new x402Client()
registerCommerceEvmScheme(client, { signer, networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(client, { signer, networks: 'eip155:84532' })
```

### Server

```typescript
import { CommerceServerScheme, registerCommerceEvmScheme } from '@x402r/evm/commerce/server'
import {
AuthCaptureServerScheme,
registerAuthCaptureEvmScheme,
} from '@x402r/evm/authCapture/server'
import { x402ResourceServer } from '@x402/core/server'

const server = new x402ResourceServer(facilitatorConfig)
registerCommerceEvmScheme(server, { networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(server, { networks: 'eip155:84532' })
```

### Facilitator

The commerce scheme integrates with x402's facilitator via `registerCommerceEvmScheme()`, using the same `FacilitatorEvmSigner` as x402's exact scheme:
The authCapture scheme integrates with x402's facilitator via `registerAuthCaptureEvmScheme()`, using the same `FacilitatorEvmSigner` as x402's exact scheme:

```typescript
import { x402Facilitator } from '@x402/core/facilitator'
import { toFacilitatorEvmSigner } from '@x402/evm'
import { registerCommerceEvmScheme } from '@x402r/evm/commerce/facilitator'
import { registerAuthCaptureEvmScheme } from '@x402r/evm/authCapture/facilitator'

const evmSigner = toFacilitatorEvmSigner({ address, ...clients })

const facilitator = new x402Facilitator()
registerCommerceEvmScheme(facilitator, { signer: evmSigner, networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(facilitator, { signer: evmSigner, networks: 'eip155:84532' })
```

## Development
Expand Down
4 changes: 4 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export default [
TextEncoder: 'readonly',
crypto: 'readonly',
setTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
console: 'readonly',
process: 'readonly',
NodeJS: 'readonly',
},
},
plugins: {
Expand Down
51 changes: 36 additions & 15 deletions packages/evm/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @x402r/evm

Commerce payment scheme for x402 HTTP 402 flows. Bridges the x402 protocol with x402r commerce contracts on Base.
AuthCapture payment scheme for x402 HTTP 402 flows. Bridges the x402 protocol with x402r commerce contracts on Base.

## Install

Expand All @@ -13,46 +13,67 @@ npm install @x402r/evm
### Client — Create payment payloads

```typescript
import { CommerceEvmScheme, registerCommerceEvmScheme } from '@x402r/evm/commerce/client'
import { AuthCaptureEvmScheme, registerAuthCaptureEvmScheme } from '@x402r/evm/authCapture/client'
import { x402Client } from '@x402/core/client'

const client = new x402Client()
registerCommerceEvmScheme(client, { signer })
registerAuthCaptureEvmScheme(client, { signer })
// or with specific networks:
registerCommerceEvmScheme(client, { signer, networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(client, { signer, networks: 'eip155:84532' })
```

### Server — Register with x402 resource server

```typescript
import { CommerceServerScheme, registerCommerceEvmScheme } from '@x402r/evm/commerce/server'
import {
AuthCaptureServerScheme,
registerAuthCaptureEvmScheme,
} from '@x402r/evm/authCapture/server'
import { x402ResourceServer } from '@x402/core/server'

const server = new x402ResourceServer(facilitatorConfig)
registerCommerceEvmScheme(server)
registerAuthCaptureEvmScheme(server)
// or with specific networks:
registerCommerceEvmScheme(server, { networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(server, { networks: 'eip155:84532' })
```

### Facilitator — Verify and settle payments

```typescript
import {
CommerceFacilitatorScheme,
registerCommerceEvmScheme,
} from '@x402r/evm/commerce/facilitator'
AuthCaptureFacilitatorScheme,
registerAuthCaptureEvmScheme,
} from '@x402r/evm/authCapture/facilitator'
import { x402Facilitator } from '@x402/core/facilitator'

const facilitator = new x402Facilitator()
registerCommerceEvmScheme(facilitator, { signer, networks: 'eip155:84532' })
registerAuthCaptureEvmScheme(facilitator, { signer, networks: 'eip155:84532' })
```

## Exports

- `@x402r/evm` — `CommerceEvmScheme` (client scheme class)
- `@x402r/evm/commerce/client` — `CommerceEvmScheme`, `registerCommerceEvmScheme()`, `EvmClientConfig`
- `@x402r/evm/commerce/server` — `CommerceServerScheme`, `registerCommerceEvmScheme()`, `EvmResourceServerConfig`
- `@x402r/evm/commerce/facilitator` — `CommerceFacilitatorScheme`, `registerCommerceEvmScheme()`, `EvmFacilitatorConfig`
- `@x402r/evm` — `AuthCaptureEvmScheme` (client scheme class)
- `@x402r/evm/authCapture/client` — `AuthCaptureEvmScheme`, `registerAuthCaptureEvmScheme()`, `EvmClientConfig`
- `@x402r/evm/authCapture/server` — `AuthCaptureServerScheme`, `registerAuthCaptureEvmScheme()`, `EvmResourceServerConfig`
- `@x402r/evm/authCapture/facilitator` — `AuthCaptureFacilitatorScheme`, `registerAuthCaptureEvmScheme()`, `EvmFacilitatorConfig`

## Testing

```bash
pnpm test # unit tests (mock-only, network-free)
pnpm test:fork # fork tests (require BASE_SEPOLIA_RPC_URL)
```

Fork tests spawn a local anvil instance forked from Base Sepolia and exercise the full settle path against the canonical `AuthCaptureEscrow` and token-collector deploys. They cover `{authorize, charge} × {eip3009, permit2}` and assert post-settle on-chain state via `escrow.paymentState(hash)`.

Required env:

| Var | Purpose | Default |
| :------------------------ | :------------------------------------------------------------------ | :------------------------------------------------------------------------ |
| `BASE_SEPOLIA_RPC_URL` | Upstream RPC anvil forks from. Any working Base Sepolia endpoint. | _required_ — fork tests skip cleanly if unset |
| `ANVIL_BIN` | Path to the `anvil` binary. | `anvil` (must be on `PATH`; install via [Foundry](https://getfoundry.sh)) |
| `BASE_SEPOLIA_FORK_BLOCK` | Pin the fork to a specific block (recommended for reproducibility). | _unset_ — uses chain head |
| `ANVIL_VERBOSE` | If set, anvil's stdout/stderr is inherited (default: silenced). | _unset_ |

## Links

Expand Down
35 changes: 18 additions & 17 deletions packages/evm/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@x402r/evm",
"version": "0.1.0",
"description": "Commerce payment scheme for x402 using Base Commerce Payments",
"version": "0.2.0",
"description": "AuthCapture payment scheme for x402 using Base Commerce Payments",
"license": "MIT",
"author": "x402r",
"repository": {
Expand All @@ -20,24 +20,24 @@
"default": "./dist/cjs/index.js"
}
},
"./commerce/client": {
"./authCapture/client": {
"import": {
"types": "./dist/esm/commerce/client/index.d.mts",
"default": "./dist/esm/commerce/client/index.mjs"
"types": "./dist/esm/authCapture/client/index.d.mts",
"default": "./dist/esm/authCapture/client/index.mjs"
},
"require": {
"types": "./dist/cjs/commerce/client/index.d.ts",
"default": "./dist/cjs/commerce/client/index.js"
"types": "./dist/cjs/authCapture/client/index.d.ts",
"default": "./dist/cjs/authCapture/client/index.js"
}
},
"./commerce/server": {
"./authCapture/server": {
"import": {
"types": "./dist/esm/commerce/server/index.d.mts",
"default": "./dist/esm/commerce/server/index.mjs"
"types": "./dist/esm/authCapture/server/index.d.mts",
"default": "./dist/esm/authCapture/server/index.mjs"
},
"require": {
"types": "./dist/cjs/commerce/server/index.d.ts",
"default": "./dist/cjs/commerce/server/index.js"
"types": "./dist/cjs/authCapture/server/index.d.ts",
"default": "./dist/cjs/authCapture/server/index.js"
}
},
"./extensions/attestation": {
Expand All @@ -50,14 +50,14 @@
"default": "./dist/cjs/extensions/attestation/index.js"
}
},
"./commerce/facilitator": {
"./authCapture/facilitator": {
"import": {
"types": "./dist/esm/commerce/facilitator/index.d.mts",
"default": "./dist/esm/commerce/facilitator/index.mjs"
"types": "./dist/esm/authCapture/facilitator/index.d.mts",
"default": "./dist/esm/authCapture/facilitator/index.mjs"
},
"require": {
"types": "./dist/cjs/commerce/facilitator/index.d.ts",
"default": "./dist/cjs/commerce/facilitator/index.js"
"types": "./dist/cjs/authCapture/facilitator/index.d.ts",
"default": "./dist/cjs/authCapture/facilitator/index.js"
}
}
},
Expand All @@ -74,6 +74,7 @@
"clean": "rm -rf dist",
"test": "vitest run",
"test:watch": "vitest",
"test:fork": "vitest run --config vitest.fork.config.ts",
"typecheck": "tsc --noEmit",
"lint": "eslint . --fix",
"lint:check": "eslint .",
Expand Down
3 changes: 3 additions & 0 deletions packages/evm/src/authCapture/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { AuthCaptureEvmScheme } from './scheme'
export { registerAuthCaptureEvmScheme } from './register'
export type { EvmClientConfig } from './register'
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import type { Network } from '@x402/core/types'
import type { ClientEvmSigner } from '@x402/evm'
import { x402Client } from '@x402/core/client'
import { CommerceEvmScheme } from './scheme'
import { AuthCaptureEvmScheme } from './scheme'

export interface EvmClientConfig {
signer: ClientEvmSigner
networks?: Network | Network[]
}

/**
* Register commerce client scheme with x402Client
* Register authCapture client scheme with x402Client
*
* @example
* ```typescript
* const client = new x402Client();
* registerCommerceEvmScheme(client, { signer });
* registerAuthCaptureEvmScheme(client, { signer });
* // or with specific networks:
* registerCommerceEvmScheme(client, { signer, networks: "eip155:84532" });
* registerAuthCaptureEvmScheme(client, { signer, networks: "eip155:84532" });
* ```
*/
export function registerCommerceEvmScheme(client: x402Client, config: EvmClientConfig): x402Client {
const scheme = new CommerceEvmScheme(config.signer)
export function registerAuthCaptureEvmScheme(
client: x402Client,
config: EvmClientConfig,
): x402Client {
const scheme = new AuthCaptureEvmScheme(config.signer)
const networks = config.networks
? Array.isArray(config.networks)
? config.networks
Expand Down
Loading
Loading